13.2.2 Examples of Code Assuming Wraparound Overflow

There was long a tension between what the C standard requires for signed integer overflow, and what traditional C programs commonly assumed. The standard allows aggressive optimizations based on assumptions that overflow never occurs, but traditionally many C programs relied on overflow wrapping around. Although these programs did not conform to the standard, they formerly worked in practice because traditionally compilers did not optimize in such a way that would break the programs. Nowadays, though, compilers do perform these optimizations, so portable programs can no longer assume reliable wraparound on signed integer overflow.

The C Standard says that if a program has signed integer overflow its behavior is undefined, and the undefined behavior can even precede the overflow. To take an extreme example:

if (password == expected_password)
  allow_superuser_privileges ();
else if (counter++ == INT_MAX)
  abort ();
else
  printf ("%d password mismatches\n", counter);

If the int variable counter equals INT_MAX, counter++ must overflow and the behavior is undefined, so the C standard allows the compiler to optimize away the test against INT_MAX and the abort call. Worse, if an earlier bug in the program lets the compiler deduce that counter == INT_MAX or that counter previously overflowed, the C standard allows the compiler to optimize away the password test and generate code that allows superuser privileges unconditionally.

Here is an example derived from the 7th Edition Unix implementation of atoi (1979-01-10):

char *p;
int f, n;
…
while (*p >= '0' && *p <= '9')
  n = n * 10 + *p++ - '0';
return (f ? -n : n);

Even if the input string is in range, on most modern machines this has signed overflow when computing the most negative integer (the -n overflows) or a value near an extreme integer (the + overflows).

Here is another example, derived from the 7th Edition implementation of rand (1979-01-10). Here the programmer expects both multiplication and addition to wrap on overflow:

static long int randx = 1;
…
randx = randx * 1103515245 + 12345;
return (randx >> 16) & 077777;

In the following example, derived from the GNU C Library 2.15 implementation of mktime (2012-03-21), the code assumes wraparound arithmetic in + to detect signed overflow:

time_t t, t1, t2;
int sec_requested, sec_adjustment;
…
t1 = t + sec_requested;
t2 = t1 + sec_adjustment;
if (((t1 < t) != (sec_requested < 0))
    | ((t2 < t1) != (sec_adjustment < 0)))
  return -1;

Although some of these examples will likely behave as if signed integer overflow wraps around reliably, other examples are likely to misbehave when optimization is enabled. All these examples should be avoided in portable code because signed integer overflow is not reliable on modern systems, and it’s not worth worrying about which of these examples happen to work on most platforms and which do not.