...
This non-compliant code example shows a double-free vulnerability resulting from memory being allocated and freed at differing levels of abstraction. In this example, memory for the list
array is allocatd in the process_list()
function. The array is then passed to the verify_list()
function that performs error checking on the size of the list. If the size of the list is below a minimum size, the memory allocated to the list is freed and the function returns to the caller. The calling function then frees this same memory again, resulting in a double-free and potentially exploitable vulnerability.
Code Block | ||
---|---|---|
| ||
enum { MIN_SIZE_ALLOWED = 32 };
int verify_size(char *list, size_t size) {
if (size < MIN_SIZE_ALLOWED) {
/* Handle Error Condition */
free(list);
return -1;
}
return 0;
}
void process_list(size_t number) {
char *list = (char *)malloc(number);
if (list == NULL) {
/* Handle Allocation Error */
}
if (verify_size(list, number) == -1) {
free(list);
return;
}
/* Continue Processing list */
free(list);
}
|
...
To correct this problem, the the error handling code in verify_list()
is modified so that it no longer frees list
. This change ensures that list
is freed only once, at the same level of abstraction, in the process_list()
function.
Code Block | ||
---|---|---|
| ||
enum { MIN_SIZE_ALLOWED = 32 };
int verify_size(char const *list, size_t size) {
if (size < MIN_SIZE_ALLOWED) {
/* Handle Error Condition */
return -1;
}
return 0;
}
void process_list(size_t number) {
char *list = (char *)malloc(number);
if (list == NULL) {
/* Handle Allocation Error */
}
if (verify_size(list, number) == -1) {
free(list);
return;
}
/* Continue Processing list */
free(list);
}
|
...