...
This noncompliant 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 allocated in the process_list()
function. The array is then passed to the verify_size()
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 error handling code in verify_size()
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(const char *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); } |
...