...
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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
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'\\'; 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. | ||||||||
| 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. | |||||||
| ABV.ITERATOR SV.TAINTED.LOOP_BOUND | ||||||||
LDRA tool suite |
| 47 S | Partially implemented. | ||||||
PRQA QA-C |
| 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].
...