UB ? Checking size of off_t

M

mathieu

Dear all,

Could someone please confirm whether the following code is UB or not ?

#include <sys/types.h>

int main(int argc, char **argv)
{
/* Cause a compile-time error if off_t is smaller than 64 bits */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ];
return 0;
}

It does not compile using clang 3.0 (as expected), however it does compile using gcc 4.6 and gcc 4.7.
 
J

James Kuyper

Dear all,

Could someone please confirm whether the following code is UB or not ?

#include <sys/types.h>

The C standard does not define a standard header with that name, nor
does it define an identifier "off_t", as used below. The contents of
that header (and in particular, the definition it provides for off_t)
determine whether or not this code has undefined behavior.

The POSIX standard does define such a header, which does define a type
named off_t. If you're relying upon that standard, the answers I give
below should be relevant, but you'll get better answers to your question
in a forum devoted to it, such as comp.unix.programmer.
int main(int argc, char **argv)
{
/* Cause a compile-time error if off_t is smaller than 64 bits */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))

The << operator has undefined behavior unless the left operand is
unsigned, or is a signed type capable of representing (in this case) 1^62.
I have access to a copy of the POSIX standard, but I haven't been able
to locate a precise specification of the requirements for off_t, only
requirements that it be defined in various headers - I might not be
looking in the right place. I did find that POSIX allows for the
possibility that off_t is only 32 bits, which can be tested by checking
whether _POSIX_V6_ILP32_OFF32 or _POSIX_V7_ILP32_OFF32 are #defined in
<unistd.h>, or by passing _SC_V6_ILP32_OFF32 or _SC_POSIX_V7_ILP32_OFF32
to sysconf(), or by passing the corresponding _CS* strings to confstr().

Given the way that it's used, I suspect that off_t is required to be a
signed type, which is in fact the case on my system. If it's also 32
bits, then (off_t)1 << 62 has undefined behavior.
Other than that issue, I don't see any other problems with that expression.
int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ];
return 0;
}

It does not compile using clang 3.0 (as expected), however it does compile using gcc 4.6 and gcc 4.7.

I think that the -1 subscript was intended to create a constraint
violation, for which a diagnostic is required, which would make it
reasonable to expect the compilation to fail (though, strictly speaking,
an implementation is allowed to continue compiling if it wants to).
However, unless off_t is allowed to be unsigned, which seems unlikely,
the result of -1 cannot occur if evaluation of the shift expressions has
defined behavior. If the behavior is undefined, a diagnostic is not
mandatory, and you cannot justify expecting the compilation to fail.
 
E

Eric Sosman

Dear all,

Could someone please confirm whether the following code is UB or not ?

#include <sys/types.h>

int main(int argc, char **argv)
{
/* Cause a compile-time error if off_t is smaller than 64 bits */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ];
return 0;
}

It does not compile using clang 3.0 (as expected), however it does compile using gcc 4.6 and gcc 4.7.

There's U.B. if off_t (not a C-defined type, by the way) is in
fact narrow. If it is, the sub-expression `(off_t) 1 << 62)' runs
afoul of 6.5.7p3:

"[...] If the value of the right operand is [...] greater
than or equal to the width of the promoted left operand,
the behavior is undefined."
 
J

James Kuyper

On 03/26/2014 01:22 PM, James Kuyper wrote:
.....
unsigned, or is a signed type capable of representing (in this case) 1^62.

Well, that's not a hard requirement to meet :-} That was intended to be
2^62.
 
M

mathieu

Dear all,

Could someone please confirm whether the following code is UB or not ?

#include <sys/types.h>

int main(int argc, char **argv)

/* Cause a compile-time error if off_t is smaller than 64 bits */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ];
return 0;


It does not compile using clang 3.0 (as expected), however it does compile using gcc 4.6 and gcc 4.7.



There's U.B. if off_t (not a C-defined type, by the way) is in

fact narrow. If it is, the sub-expression `(off_t) 1 << 62)' runs

afoul of 6.5.7p3:



"[...] If the value of the right operand is [...] greater

than or equal to the width of the promoted left operand,

the behavior is undefined."

Neat ! Thanks for the clear answer.
 
T

Tim Rentsch

mathieu said:
Could someone please confirm whether the following code is UB or not ?

#include <sys/types.h>

int main(int argc, char **argv)
{
/* Cause a compile-time error if off_t is smaller than 64 bits */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ];
return 0;
}

It does not compile using clang 3.0 (as expected), however it does
compile using gcc 4.6 and gcc 4.7.

Is there some reason you can't just do, eg,

#define BITS_63 9223372036854775807
extern char staticly_assert[ (off_t) BITS_63 == BITS_63 ? 1 : -1 ];

This should work as desired on any implementation that has an
integer type having at least 64 bits, and definitely give a
diagnostic on any implementation that doesn't (in which case
'off_t' is almost certainly not a 64-bit type).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,898
Latest member
BlairH7607

Latest Threads

Top