...
In this noncompliant example, a diagnostic is required because realloc()
may free c_str1
when it returns a null pointer, resulting in c_str1
being freed twice. The committee response to Defect Report #400 makes it implementation-defined whether the old object is deallocated if size
is zero and memory for the new object is not allocated, and the current implementation of realloc()
in glibc will free c_str1
and return a null pointer for zero byte allocations. Freeing a pointer twice can result in a potentially exploitable vulnerability commonly referred to as a "double-free exploit" [Seacord 2013].
Code Block |
---|
|
#include <stdlib.h>
void f(char *c_str1, size_t size) {
char *c_str2 = (char *)realloc(c_str1, size);
if (c_str2 == NULL) {
free(c_str1); // diagnostic required
return;
}
} |
Compliant Solution
This compliant solution does not pass a size argument of zero to the realloc()
function, eliminating the possibility of c_str1
being freed twice.
Code Block |
---|
|
#include <stdlib.h>
void f(char *c_str1, size_t size) {
if (size != 0) {
char *c_str2 = (char *)realloc(c_str1, size);
if (c_str2 == NULL) {
free(c_str1);
}
}
return;
} |
Noncompliant Code Example
In this noncompliant example (CVE-2009-1364) from libwmf
version 0.2.8.4, the return value of gdRealloc
(a simple wrapper around realloc
that reallocates space pointed to by im->clip->list
) is set to more
. However, the value of im->clip->list
is used directly afterwards in the code, and the C Standard specifies that if realloc
moves the area pointed to, then the original is freed. An attacker can then execute arbitrary code by forcing a reallocation (with a sufficient im->clip->count
) and accessing freed memory [xorl 2009].
Code Block |
---|
|
void gdClipSetAdd(gdImagePtr im,gdClipRectanglePtr rect) {
gdClipRectanglePtr more;
if (im->clip == 0) {
/* ... */
}
if (im->clip->count == im->clip->max) {
more = gdRealloc (im->clip->list,(im->clip->max + 8) *
sizeof (gdClipRectangle));
/*
* If the realloc fails, then we have not lost the
* im->clip->list value.
*/
if (more == 0) return;
im->clip->max += 8;
}
im->clip->list[im->clip->count] = (*rect);
im->clip->count++;
|
Compliant Solution
The compliant solution simply reassigns im->clip->list
to the value of more
after the call to realloc
:
Code Block |
---|
|
void gdClipSetAdd(gdImagePtr im,gdClipRectanglePtr rect) {
gdClipRectanglePtr more;
if (im->clip == 0) {
/* ... */
}
if (im->clip->count == im->clip->max) {
more = gdRealloc (im->clip->list,(im->clip->max + 8) *
sizeof (gdClipRectangle));
if (more == 0) return;
im->clip->max += 8;
im->clip->list = more;
}
im->clip->list[im->clip->count] = (*rect);
im->clip->count++;
|
Noncompliant Code Example
In this example, an object is referred to outside of its lifetime:
Code Block |
---|
|
int *get_ptr(void) {
int obj = 12;
return &obj;
}
void func(void) {
int *ptr = get_ptr();
*ptr = 42;
} |
Compliant Solution
In this compliant solution, allocated storage is used instead of automatic storage for the pointer:
...