Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Signed integer overflow causes undefined behavior. The following is one possible scenario that shows why this should be avoided: Wiki MarkupThe {{size_t}} type is typically represented by the same number of bits as {{int}}, that is, {{are two possible conditions under which this code constitutes a serious vulnerability:

sizeof(size_t)

...

==

...

sizeof(int))

unmigrated-wiki-markup
Wiki Markup
The unsigned }}.  In this case, {{n}} might bemay contain a value greater than {{INT_MAX}}.  Assuming quiet wraparound on signed overflow, the loop executes {{n}} times because the comparison {{i < n}} is an unsigned comparison. Once {{i > INT_MAX}}, {{i}} takes on negative values starting with ({{INT_MIN}}).  Consequently, the memory locations referenced by {{p\[i\]}} precede the memory referenced by {{p}} and a write-outside-array bounds occurs.
Under the same assumption, if {{size_t}} is represented by a greater number of bits than {{int}}, that is, {{

sizeof(size_t)

...

>

...

sizeof(int)

Wiki Markup
Similar behavior as}}, the samecase behaviorabove occurs for values of {{n <= UINT_MAX}}. For values of {{n > UINT_MAX}}, all of memory within {{\[INT_MIN, INT_MAX\]}} from the beginning of the output buffer is overwritten in an infinite loop.  This is because the expression {{\++i}} will wrap around to zero before the condition {{i < n}} ever evaluates to false.

Note that in a preemptive multithreaded program, only one thread is in the infinite loop, so it is still significant that out-of-bounds memory is changed.

...

 This causes all memory within {{\[INT_MIN, INT_MAX\]}} from the beginning of the output buffer is overwritten in an infinite loop.

Compliant Solution (TR 24731-1)

Declaring i to be of type rsize_t eliminates the possible integer overflow condition (in this example).  Also, the argument n is changed to be of type rsize_t to document additional validation in the form of a check against RSIZE_MAX.

...

In this non-compliant code example, an integer overflow is specifically checked for by checking whether length + 1 == 0 (that is, integer wrap around has occurred). If the test passes, a wrapper to malloc() is called to allocate the appropriate data block (this is a common idiom). In a program compiled using an ILP32 compiler, this code runs as expected, but in an LP64 environment, an integer overflow can occur because length is now a 64-bit value. The result of the expression, however, is truncated to 32 bits when passed as an argument to alloc() because it takes an unsigned int argument.

Code Block
bgColor#FFcccc
void *alloc(unsigned int blocksize) {
  return malloc(blocksize);
}

int read_counted_string(int fd) {
  unsigned long length;
  unsigned char *data;

  if (read_integer_from_network(fd, &length) < 0) {
    return -1;
  }

  if (length + 1 == 0) {
    /* handle integer overflow */
  }

  data = (unsigned char*)alloc(length + 1);

  if (read_network_data(fd, data, length) < 0) {
    free(data);
    return -1;
  }
  data[length] = '\0';

  /* ... */
  free( data);
  return 0;
}

Compliant Solution (TR 24731-1)

Declaring both length and the blocksize argument to alloc() as rsize_t eliminates the possibility of truncation.

...