The size_t type is the unsigned integer type of the result of the sizeof operator. Variables of type size_t are guaranteed to be of sufficient precision to represent the size of an object. The limit of size_t is specified by the SIZE_MAX macro.
| The type {{size_t}} generally covers the entire address space.  \[[TR 24731-1|AA. C References#ISO/IEC TR 24731-1-2007]\] introduces a new type {{rsize_t}}, defined to be {{size_t}} but explicitly used to hold the size of a single object.  In code that documents this purpose by using the type {{rsize_t}}, the size of an object can be checked to verify that it is no larger than {{RSIZE_MAX}}, the maximum size of a normal single object, which provides additional input validation for library functions.  See \[[STR00-A. Use TR 24731 for remediation of existing string manipulation code]\] for additional discussion of TR 24731-1. | 
Any variable that is used to represent the size of an object including integer values used as sizes, indices, loop counters, and lengths should be declared as rsize_t if available, otherwise size_t.
In this non-compliant code example, the dynamically allocated buffer referenced by p overflows for values of n > INT_MAX.
| 
char *copy(size_t n, const char *str) {
  int i;
  char *p = (char *)malloc(n);
  if (p == NULL) {
    /* Handle malloc failure */
  }
  for ( i = 0; i < n; ++i ) {
    p[i] = *str++;
  }
  return p;
}
char *p = copy(9, "hi there");
 | 
| The {{size_t}} type is typically represented by the same number of bits as {{int}}, that is, {{sizeof(size_t) == sizeof(int))}}.  Assuming quiet wraparound on signed overflow, {{n}} might be greater than {{INT_MAX}}.  The loop, however, 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. | 
| If {{size_t}} is represented by a greater number of bits than {{int}}, {{that is sizeof(size_t) > sizeof(int)}}, the same behavior 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 are 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.
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.
| 
char *copy(rsize_t n, const char *str) {
  rsize_t i;
  char *p;
  if (n > RSIZE_MAX) {
    /* Handle unreasonable object size error */
  }
  p = (char *)malloc(n);
  if (p == NULL) {
    /* Handle malloc failure */
  }
  for ( i = 0; i < n; ++i ) {
    p[i] = *str++;
  }
  return p;
}
char *p = copy(9, "hi there");
 | 
In this non-compliant code example, an integer overflow is specifically checked for by checking if length + 1 == 0 (that is, integer wrap 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. Tthe result of the expression, however, is truncated to 32-bits when passed as an argument to alloc(), because it takes an unsigned int argument.
| 
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';
  /* ... */
}
 | 
Declaring both length and the blocksize argument to alloc() as rsize_t eliminates the possibility of truncation.
| 
void *alloc(rsize_t blocksize) {
  if (blocksize > RSIZE_MAX) {
    /* Handle error */
  }
  return malloc(blocksize);
}
int read_counted_string(int fd) {
  rsize_t 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';
  /* ... */
}
 | 
The improper calculation or manipulation of an object's size can result in exploitable vulnerabilities.
| Recommendation | Severity | Likelihood | Remediation Cost | Priority | Level | 
|---|---|---|---|---|---|
| INT01-A | 2 (medium) | 2 (probable) | 2 (medium) | P8 | L2 | 
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
| \[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]\] Section 7.17, "Common definitions <stddef.h>" \[[ISO/IEC 9899-1999|AA. C References#ISO/IEC 9899-1999]\] Section 7.20.3, "Memory management functions" \[[ISO/IEC TR 24731-1-2007|AA. C References#ISO/IEC TR 24731-1-2007]\] | 
INT00-A. Understand the data model used by your implementation(s) 04. Integers (INT) INT02-A. Understand integer conversion rules