...
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
...