2

For example:

size_t x;
...
__builtin_uaddll_overflow(x,1,&x);

Would the above code correctly guard against integer overflow regardless of compiler implementation?

What I know so far:

  • This reference states that size_t is an unsigned type.
  • According to this discussion, typedef unsigned long size_t; may be used to define size_t.

Is there a function listed at this reference that will always be correct? Or will it necessarily depend on the specific implementation? If so, how could I programatically choose the correct function?

David Varela
  • 5,140
  • 15
  • 32

1 Answers1

6
size_t x;
...
__builtin_uaddll_overflow(x,1,&x);

Would the above code correctly guard against integer overflow regardless of compiler implementation?

No. __builtin_uaddll_overflow() is not a specified C operator nor a C standard library function. It restricts code to select compilers. The functionality of __builtin_uaddll_overflow() is not specified by C.

Instead, simply compare against SIZE_MAX for a portable implementation. It is portable regardless if size_t is the same as unsigned, unsigned long or some other unsigned type, even wider or narrower than unsigned.

size_t x;
size_t y;
if (SIZE_MAX - x < y) Overflow();
else size_t sum = x + y;

This works for the various unsigned types too - even narrow ones. Use the same some_unsigned_type throughout.

some_unsigned_type x;
some_unsigned_type y;
if (some_unsigned_type_MAX - x < y) Overflow();
else some_unsigned_type sum = x + y;

When working with unsigned types at least as wide as unsigned, code could use the following.

some_at_least_unsigned_type x;
some_at_least_unsigned_type y;
some_at_least_unsigned_type sum = x + y;  // overflow behavior well defined
if (sum < x) {
  Overflow();
}
else {
  // continue with non-overflowed sum
} 

In the case of size_t, size_t is very commonly as wide or wider than unsigned, although not specified to be so.


The above usually works with unsigned types narrower then unsigned. Yet the x + y is done with int math then and unicorn platforms could then overflow int math. 1u*x + y or 0u + x + y forces the math to always be done as unsigned, regardless if some_unsigned_type is narrower/wider than unsigned. A good compiler will emit optimize code that does not perform an actual 1u* multiplication.

some_unsigned_type x;
some_unsigned_type y;
some_unsigned_type sum = 1u*x + y;  // overflow behavior well defined
if (sum < x) {
  Overflow();
}
else {
  // continue with non-overflowed sum
} 

Or in OP's + 1 example, to insure unsigned math addition:

size_t x;
size_t sum = x + 1u;  // overflow behavior well defined
if (sum == 0) {
  Overflow();
}
else {
  // continue with non-overflowed sum
} 
chux - Reinstate Monica
  • 113,725
  • 11
  • 107
  • 213
  • It might also be useful to note that in most systems, one can use `#if SIZE_MAX <= UINT_MAX` (use unsigned int) `#elif SIZE_MAX <= ULONG_MAX` (use unsigned long) `#else` (use unsigned long long) `#endif` for code paths that absolutely need to know which of the three unsigned integer types suffices to describe all values of `size_t` type. One use case is to add aliases (macros) for compiler built-in C extensions for `size_t`-compatible unsigned integer types (and `ssize_t`-compatible signed integer types, too). – Nominal Animal Jan 29 '18 at 05:47