Double Free
Double Free is a bug caused by freeing the same memory address twice. This is generally characterised by a process failure which can create an availability risk, or by a memory corruption that can lead to a write-what-where condition allowing an attacker to execute arbitrary code which can create an integrity, confidentiality or availability risk (CWE-415).
A simple example is the code below:
#include <stdlib.h>
int main(void) {
int* p = malloc(sizeof(int));
free(p);
free(p); // Double free, what could go wrong?
return EXIT_SUCCESS;
}
Detection and Debugging Tools
To detect this bug, it is possible to use static code analysis tools such as Clang Static Analyzer or Cppcheck (for the C language) or fuzzers such as AFL++ (for the C language), although these tools do not detect all occurrences of the problem.
In the event of a failure, it is therefore also necessary to have debugging tools such as GDB or Valgrind (for the C language), that can identify the instruction causing the problem.
A more drastic solution to address this bug is to use languages that automatically manage memory at compile time or that use a garbage collector.
Exercise
The exercise program contains the implementation of some functions and, unfortunately, it doesn’t work. Your goal is:
- to find the bug using debugging tools;
- and to fix it.
The Case of Rust
In Rust, this bug is not possible thanks to its Ownership feature. It allows Rust’s memory to be managed automatically during compilation so Double Free bugs are prevented.
Here is an example of Rust code that shows this feature:
fn main() {
// The string "Hello World!" is allocated on the heap here
// and is owned by the variable `my_string`.
let my_string = String::from("Hello World!");
println!("{}", my_string);
} // The string "Hello World!" is automatically deallocated from the heap here
// because the variable `my_string` that owns it goes out of scope.
It is the equivalent of the following C code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
// The string "Hello World!" is allocated on the heap here
// and its reference is stored in the pointer `my_string`.
char* my_string = malloc(sizeof("Hello World!"));
strcpy(my_string, "Hello World!");
printf("%s\n", my_string);
// The string "Hello World!" is manually deallocated from the heap here
// using the pointer `my_string`.
free(my_string);
return EXIT_SUCCESS;
}
This example cleary shows that the deallocation of memory in Rust is done automatically when variables goes out of scope instead of manually. In reality, the Ownership feature of Rust is composed of many more rules that will guarantee that the program won’t have any memory-safety bug but the final goal is the same.