Versions Compared

Key

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

...

One compliant solution is to detect and reject invalid values of index if using them in pointer arithmetic would result in an invalid pointer.:

Code Block
bgColor#ccccff
langc
enum { TABLESIZE = 100 };

static int table[TABLESIZE];

int* f(int index) {
  if (0 <= index && index < TABLESIZE)
    return table + index;

  return NULL;
}

...

Another, slightly simpler and potentially more efficient compliant solution is to use an unsigned type to avoid having to check for negative values while still rejecting out-of-bounds positive values of index.:

Code Block
bgColor#ccccff
langc
enum { TABLESIZE = 100 };

static int table[TABLESIZE];

int* f(size_t index) {
  if (index < TABLESIZE)
    return table + index;

  return NULL;
}

...

The following compliant solution correctly validates the index pos by using the <= operator and avoids modifying size until it has verified that the call to realloc() was successful.:

Code Block
bgColor#ccccff
langc
static int *table = NULL;
static size_t size = 0;

int insert_in_table(size_t pos, int value) {
  if (size <= pos) {
    int *tmp = (int*)realloc(table, sizeof *table * (pos + 1));
    if (NULL == tmp)
      return -1;   /* indicate failure */

    /* Modify size only after realloc succeeds */
    size  = pos + 1;
    table = tmp;
  }

  table[pos] = value;
  return 0;
}

...

The following compliant solution avoids using out-of-range indices by initializing matrix elements in the same row-major order as multidimensional objects are declared in C.:

Code Block
bgColor#ccccff
langc
static const size_t COLS = 5;
static const size_t ROWS = 7;

static int matrix[ROWS][COLS];

void init_matrix(int x) {
  for (size_t i = 0; i != ROWS; ++i)
    for (size_t j = 0; j != COLS; ++j)
      matrix[i][j] = x;
}

...

The following compliant solution avoids incrementing the pointer unless a value past the pointer's current value is known to exist.:

Code Block
bgColor#ccccff
langc
struct S {
  size_t len;
  char   buf[];   /* flexible array member */
};

char* find(const struct S *s, int c) {
  char *first = s->buf;
  char *last  = s->buf + s->len;

  while (first != last)   /* avoid incrementing here */
    if (*++first == (unsigned char)c)
      return first;

  return NULL;
}

void g() {
  struct S *s = (struct S*)malloc(sizeof (struct S));
  s->len = 0;
  /* ... */
  char *where = find(s, '.');
  /* ... */
}

...

The following compliant solution correctly computes the maximum number of items for fread() to read from the file.:

Code Block
bgColor#ccccff
langc
void f(FILE *file) {
  wchar_t wbuf[BUFSIZ];

  const size_t size = sizeof *wbuf;
  const size_t nitems = sizeof wbuf / size;

  size_t nread;

  nread = fread(wbuf, size, nitems, file);
  /* ... */
}

...

In this noncompliant example, the integer skip is scaled when added to the pointer s and may point outside the bounds of the object referenced by s.:

Code Block
bgColor#ffcccc
langc
struct big {
  unsigned long long ull_1;
  unsigned long long ull_2;
  unsigned long long ull_3;
  int si_4;
  int si_5;
};
 
void g(void) {
  size_t skip = offsetof(struct big, ull_2);
  struct big *s = (struct big *)malloc(4 * sizeof(struct big));
  if (!s) {
    /* ... */
  }
 
  memset(s + skip, 0, sizeof(struct big) - skip);  /* violation */
 
  /* ... */
}

...

The following compliant solution does not scale skip.:

Code Block
bgColor#ccccff
langc
struct big {
  unsigned long long ull_1;
  unsigned long long ull_2;
  unsigned long long ull_3;
  int si_4;
  int si_5;
};
 
void g(void) {
  size_t skip = offsetof(struct big, ull_2);
  struct big *s = (struct big *)malloc(4 * sizeof(struct big));
  if (!s) {
    /* ... */
  }
 
  memset(skip, 0, sizeof(struct big) - skip);  
 
  /* ... */
}

...

Tool

Version

Checker

Description

Compass/ROSE

  

Could be configured to catch violations of this rule. The way to catch the noncompliant code example is to first hunt for example code that follows this pattern:

   for (LPWSTR pwszTemp = pwszPath + 2; *pwszTemp != L'\\';
*pwszTemp++;)

In particular, the iteration variable is a pointer, it gets incremented, and the loop condition does not set an upper bound on the pointer. Once this case is handled, we can handle cases like the real noncompliant code example, which is effectively the same semantics, just different syntax

Coverity

Include Page
Coverity_V
Coverity_V

ARRAY_VS_SINGLETON

NEGATIVE_RETURNS

OVERRUN_STATIC OVERRUN_DYNAMIC

Can detect the access of memory past the end of a memory buffer/array.

Can detect when the loop bound may become negative.

Can detect the out-of-bound read/write to array allocated statically or dynamically.

Klocwork

Include Page
Klocwork_V
Klocwork_V

ABV.ITERATOR SV.TAINTED.LOOP_BOUND

 
LDRA tool suite 
Include Page
LDRA_V
LDRA_V

47 S
476 S
64 X
68 X
69 X

 Partially implemented.
PRQA QA-C
Include Page
PRQA_V
PRQA_V
3680
3681
3682
3683
3685 (U)
3686
3688
3689 (U)
3690
3692
Partially implemented.

Related Vulnerabilities

CVE-2008-1517 results from a violation of this rule. Before Mac OSX version 10.5.7, the xnu kernel accessed an array at an unverified, user-input index, allowing an attacker to execute arbitrary code by passing an index greater than the length of the array and therefore accessing outside memory [xorl 2009].

...