...
The affects of not following this recommendation are best demonstrated by an actual vulnerability. Freeing memory in different modules resulted in a vulnerability in MIT Kerberos 5 http://web.mit.edu/kerberos/advisories/MITKRB5-SA-2004-002-dblfree.txt. The problem is the MIT Kerberos 5 code contains error-handling logic, which frees memory allocated by the ASN.1 decoders if pointers to the allocated memory are non-null. However, if a detectable error occurs, the ASN.1 decoders themselves free memory which they have allocated. When some library functions receive errors from the ASN.1 decoders, they also attempt to free, causing a double-free vulnerability.
Non-compliant Code Example
...
1
This example demonstrates an error that can occur when memory is freed in different functions. First, an array of number
integers is dynamically allocated. That array, referred to by list
, and its size, number
, are then passed to func2
. If the number of elements in the array is greater than the value MIN_SIZE_ALLOWED
, the array is processed. Otherwise, it is assumed an error has occurred, list
is freed, and the function returns. If the error occurs in func2
the dynamic memory referred to by list
will be freed twice: once in func2
and again at the end of func1
.
Code Block |
---|
#define MIN_SIZE_ALLOWED 10 void func2(int *list, size_t list_size) { if (size < MIN_SIZE_ALLOWED) { /* Error Condition */ free(list); return; } /* Process list */ } int func1 (size_t number) { int *list = malloc (number * sizeof(int)); if (list == NULL) { /* Handle Allocation Error */ } func2(list,number); /* Process list */ free(list); } |
Compliant Solution
...
1
To correct this the logic in the error handling code should be changed so that list
is not freed. This change assures that list
is only freed once, in func1
.
Code Block |
---|
/* NOTE: buf must point to dynamically allocated memory */#define MIN_SIZE_ALLOWED 10 void appendfunc2(charint \*buflist, size_t count, size_t sizelist_size) { if (size < MIN_SIZE_ALLOWED) { char *line = " <- THIS IS A LINE"; size_t line_len = strlen(line/* Handle Error Condition */ return; } /* Process list */ } int func1 (size_t number) { int *list = malloc (number * sizeof(int)); if ((countlist + line_len) > size) buf = realloc(buf,count+line_len); strncat(buf,line== NULL) { /* Handle Allocation Error */ } func2(list,number); /* Process list */ free(list); } |
References
Seacord 05 Chapter 4 Dynamic Memory Management
...