...
In this noncompliant code example, integer values returned by parseint(getdata()) are stored into an array of INTBUFSIZE elements of type int called buf [Dowd 2006]. If data is available for insertion into buf (which is indicated by havedata()) and buf_ptr has not been incremented past buf + sizeof(buf), an integer value is stored at the address referenced by buf_ptr. However, the sizeof operator returns the total number of bytes in buf, which is typically a multiple of the number of elements in buf. This value is scaled to the size of an integer and added to buf. As a result, the check to make sure integers are not written past the end of buf is incorrect, and a buffer overflow is possible.
| Code Block | ||||
|---|---|---|---|---|
| ||||
int buf[INTBUFSIZE];
int *buf_ptr = buf;
while (havedata() && buf_ptr < (buf + sizeof(buf))) {
*buf_ptr++ = parseint(getdata());
}
|
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
int buf[INTBUFSIZE];
int *buf_ptr = buf;
while (havedata() && buf_ptr < &buf[INTBUFSIZE] {
*buf_ptr++ = parseint(getdata());
}
|
This solution works because the C standard guarantees Standard guarantees the address of buf[INTBUFSIZE] even though no such element exists.
...
The following example is based on a flaw in the OpenBSD operating system. An integer, skip, is added as an offset to a pointer of type struct big. The adjusted pointer is then used as a destination address in a call to memset(). However, when skip is added to the struct big pointer, it is automatically scaled by the size of struct big, which is 32 bytes (assuming 4-byte integers, 8-byte long long integers, and no structure padding). This scaling results in the call to memset() writing to unintended memory.
| Code Block | ||||
|---|---|---|---|---|
| ||||
struct big {
unsigned long long ull_1; /* typicallyTypically 8 bytes */
unsigned long long ull_2; /* typicallyTypically 8 bytes */
unsigned long long ull_3; /* typicallyTypically 8 bytes */
int si_4; /* typicallyTypically 4 bytes */
int si_5; /* typicallyTypically 4 bytes */
};
/* ... */
int f(void) {
size_t skip = offsetof(struct big, ull_2);
struct big *s = (struct big *)malloc(sizeof(struct big));
if (!s) {
return -1; /* Indicate malloc() failure */
}
memset(s + skip, 0, sizeof(struct big) - skip);
/* ... */
free(s);
s = NULL;
return 0;
} |
A similar situation occurred in OpenBSD's make command [Murenin 2007].
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
struct big {
unsigned long long ull_1; /* typicallyTypically 8 bytes */
unsigned long long ull_2; /* typicallyTypically 8 bytes */
unsigned long long ull_3; /* typicallyTypically 8 bytes */
int si_4; /* typicallyTypically 4 bytes */
int si_5; /* typicallyTypically 4 bytes */
};
/* ... */
int f(void) {
size_t skip = offsetof(struct big, ull_2);
struct big *s = (struct big *)malloc(sizeof(struct big));
if (!s) {
return -1; /* Indicate malloc() failure */
}
memset((char *)s + skip, 0, sizeof(struct big) - skip);
/* ... */
free(s);
s = NULL;
return 0;
} |
Risk Assessment
...