I often see code that doesn't consider what happens as integers approach their limits, which is more likely to trigger as time goes on, with our ever increasing file sizes etc.

Ironically, often the invalid code involves checking for these integer limits. For example the following may overflow and so is invalid:

size_t alloc = SIZE_MAX;
if (alloc + BUFSIZ < SIZE_MAX)
  proceed...
Compilers traditionally roll over to a smaller value and hence would proceed in this case erroneously. Modern compilers may assume the overflow does not occur (for signed types at least) and auto rearrange the expression as shown below. For details on signed overflow handling see: To fix this common construct so that it works portably and for both signed and unsigned, one can trivially reorganize the equation by reducing the values of both sides, like:
size_t alloc = SIZE_MAX;
if (alloc < SIZE_MAX - BUFSIZ)
  proceed...

Finding integer overflow issues

gcc actually has a plethora of options related to this. They're not enabled by default as they would show up too many issues with existing code, but for new code, one really should enable the warnings below (and any other gcc warnings really).
gcc -Wtype-limits -Wstrict-overflow=5 -fstrict-overflow -Wsign-compare

Note none of these options warn or change the operation of the above example for unsigned integers – only signed integer overflow is catered for.

[Update Sep 2013: The MIT CSAIL lab have a great paper summarising cases of the more general problem of undefined behaviour in C programs, and they have subsequently released STACK, a new tool for finding undefined behavior bugs by finding dead code, which can be used to find cases like described above. Note these "ignored code" issues can even occur with unsigned integers, which gives more importance to using automated tools like this to flag such cases.]

One can also add explicit checking at runtime to your integer operations – known as precondition testing. Paul Eggert has written convenience macros for this, which can be used like: assert (!INT_ADD_OVERFLOW (a, b));. There is also some GCC support for run time detection of signed integer overflow, with the -ftrapv option. For other runtime handling options see this excellent paper on As-if Infinitely Ranged (AIR) Integers.

Avoiding integer overflow issues

In general to address these roll over issues, we can either use reorganization like in the code example above, or promotion to a wider type (either implicitly or through casting). One should really try to avoid casting though, and use more appropriate integer types or expression reorganization if possible. Following is a more involved example where we want to round a value, with an initial naïve approach susceptible to overflow.
off_t chunk_size (off_t file_size, int n)
{
  assert (n && n <= file_size);
  return (file_size + n/2) / n; /* round() */
}
The above overflows as the file approaches OFF_T_MAX. I.E. the result will go negative as file_size + n/2 rolls over OFF_T_MAX. One might be tempted to use implicit promotion to unsigned as follows:
off_t chunk_size (off_t file_size, unsigned int n)
{
  assert (n && n <= file_size);
  return (file_size + n/2) / n; /* round() */
}
However the above will fail on 32 bit systems when _FILE_OFFSET_BITS=64. I.E. when off_t is wider than int. The examples below, show how to handle this overflow using the 3 techniques mentioned above, with the "implicit promotion" probably being most appropriate for this situation.
/* Reorganization.  */
off_t chunk_size (off_t file_size, int n)
{
  assert (n && n <= file_size);
  /* Given the above assertions, the largest file_size%n
     can be is file_size-n as know n will divide file_size
     at least once.  */
  return file_size/n + (file_size%n + n/2)/n;
}
/* Implicit promotion to wider types.  */
off_t chunk_size (off_t file_size, uintmax_t n)
{
  assert (n && n <= file_size);
  return (file_size + n/2) / n; /* round() */
}
/* Explicit casting to wider types.  */
off_t chunk_size (off_t file_size, int n)
{
  assert (n && n <= file_size);
  return ((uintmax_t) file_size + n/2) / n; /* round() */
}
© Dec 6 2010