...
Noncompliant Code Example (Dereferencing Past-the-End Pointer)
The following This noncompliant code example shows the flawed logic in the Windows Distributed Component Object Model (DCOM) Remote Procedure Call (RPC) interface that was exploited by the W32.Blaster.Worm. The error is that the while
loop in the GetMachineName()
function (used to extract the host name from a longer string) is not sufficiently bounded. When the character array pointed to by pwszTemp
does not contain the backslash character among the first MAX_COMPUTERNAME_LENGTH_FQDN + 1
elements, the final valid iteration of the loop will dereference past-the-end pointer, resulting in exploitable undefined behavior 47 . In this case, the actual exploit allowed the attacker to inject executable code into a running program. Economic damage from the Blaster worm has been estimated to be at least $525 million [Pethia 2003].
...
Code Block | ||||
---|---|---|---|---|
| ||||
error_status_t _RemoteActivation( /* ... */, WCHAR *pwszObjectName, ... ) { *phr = GetServerPath( pwszObjectName, &pwszObjectName); /* ... */ } HRESULT GetServerPath( WCHAR *pwszPath, WCHAR **pwszServerPath ){ WCHAR *pwszFinalPath = pwszPath; WCHAR wszMachineName[MAX_COMPUTERNAME_LENGTH_FQDN+1]; hr = GetMachineName(pwszPath, wszMachineName); *pwszServerPath = pwszFinalPath; } HRESULT GetMachineName( WCHAR *pwszPath, WCHAR wszMachineName[MAX_COMPUTERNAME_LENGTH_FQDN+1]) { pwszServerName = wszMachineName; LPWSTR pwszTemp = pwszPath + 2; while ( *pwszTemp != L'\\' ) *pwszServerName++ = *pwszTemp++; /* ... */ } |
Compliant Solution
In the following this compliant solution, the while
loop in the GetMachineName()
function is bounded so that the loop terminates when a backslash character is found, the null termination character (L'\0'
) is discovered, or the end of the buffer is reached. This code does not result in a buffer overflow, even if no backslash character is found in wszMachineName
.
...
Similarly to the dereferencing-past-the-end-pointer error, the function insert_in_table()
in the following this noncompliant code example uses an otherwise valid index to attempt to store a value in an element just past the end of an array.
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h> static int *table = NULL; static size_t size = 0; int insert_in_table(size_t pos, int value) { if (size < pos) { int *tmp; size = pos + 1; tmp = (int *)realloc(table, sizeof(*table) * size); if (tmp == NULL) { return -1; /* Failure */ } table = tmp; } table[pos] = value; return 0; } |
Compliant Solution
The following This compliant solution correctly validates the index pos
by using the <=
operator, ensures the multiplication will not overflow, and avoids modifying size
until it has verified that the call to realloc()
was successful:
...
Noncompliant Code Example (Apparently Accessible Out-of-Range Index)
The following This noncompliant code example declares matrix
to consist of 7 rows and 5 columns in row-major order. The function init_matrix
then iterates over all 35 elements in an attempt to initialize each to the value given by the function argument x
. However, because multidimensional arrays are declared in C in row-major order, and the function iterates over the elements in column-major order, and when the value of j
reaches the value COLS
during the first iteration of the outer loop, the function attempts to access element matrix[0][5]
. Because the type of matrix
is int[7][5]
, the j
subscript is out of range, and the access has undefined behavior 49.
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 != COLS; ++i) { for (size_t j = 0; j != ROWS; ++j) { matrix[i][j] = x; } } } |
Compliant Solution
The following This 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:
...
Noncompliant Code Example (Pointer Past Flexible Array Member)
In the following this noncompliant code example, the function find()
attempts to iterate over the elements of the flexible array member buf
, starting with the second element. However, because function g()
does not allocate any storage for the member, the expression first++
in find()
attempts to form a pointer just past the end of buf
when there are no elements. This attempt results in undefined behavior 62 .
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stdlib.h> struct S { size_t len; char buf[]; /* Flexible array member */ }; const char *find(const struct S *s, int c) { const char *first = s->buf; const char *last = s->buf + s->len; while (first++ != last) { /* Undefined behavior */ if (*first == (unsigned char)c) { return first; } } return NULL; } void g(void) { struct S *s = (struct S *)malloc(sizeof(struct S)); s->len = 0; find(s, 'a'); } |
Compliant Solution
The following This compliant solution avoids incrementing the pointer unless a value past the pointer's current value is known to exist:
...
Noncompliant Code Example (Invalid Access by Library Function)
In the following this noncompliant code example, the function f()
calls fread()
to read nitems
of type wchar_t
, each size
bytes in size, into an array of BUFSIZ
elements, wbuf
. However, the expression used to compute the value of nitems
fails to account for the fact that, unlike the size of char
, the size of wchar_t
may be greater than 1. Thus, fread()
could attempt to form pointers past the end of wbuf
and use them to assign values to nonexistent elements of the array. Such an attempt results in undefined behavior 109 . A likely manifestation of this undefined behavior is a classic buffer overflow, which is often exploitable by code injection attacks.
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stddef.h> #include <stdio.h> void f(FILE *file) { wchar_t wbuf[BUFSIZ]; const size_t size = sizeof(*wbuf); const size_t nitems = sizeof(wbuf); size_t nread; nread = fread(wbuf, size, nitems, file); } |
Compliant Solution
The following This compliant solution correctly computes the maximum number of items for fread()
to read from the file:
...
Code Block | ||||
---|---|---|---|---|
| ||||
#include <stddef.h> #include <stdlib.h> #include <string.h> struct big { unsigned long long ull_1; unsigned long long ull_2; unsigned long long ull_3; int si_4; int si_5; }; int g(void) { size_t skip = offsetof(struct big, ull_2); struct big *s = (struct big *)malloc(4 * sizeof(struct big)); if (s == NULL) { return -1; /* Failure */ } memset(s + skip, 0, sizeof(struct big) - skip); return 0; } |
Compliant Solution
The following This compliant solution does not scale skip
:
...