I'm trying to implement a secure-free function that erases the allocated memory, frees it and then also sets the pointer to the allocated region to NULL so the pointer cannot be reused after-free and cannot be double-freed with the same function. To achieve this I'm using a pointer-to-pointer parameter, which allows me to overwrite the pointer to the allocated memory.
The issue is GCC complaining about incompatible pointer types ("but it works on my machine"); I did not expect such a warning. My understanding was that any pointer can be implicitly cast to void*
, thus I'm guessing also the address of a pointer could be cast to void**
.
In the meantime I rewrote secure_free()
as a macro, which solves the warning, but I would like to know why the compiler is complaining.
File securefree.c
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#define STRING_BUFFER_LEN 10
/**
* Securely erases a heap-allocated memory section, frees it and sets its
* pointer to NULL to avoid use-after-free and double-free.
*/
static void secure_free(void** p_p_data, size_t length_in_bytes)
{
if (p_p_data == NULL || *p_p_data == NULL)
{ return; }
memset(*p_p_data, 0, length_in_bytes);
free(*p_p_data);
*p_p_data = NULL;
}
int main(void)
{
// Allocate some data
char* my_string = calloc(STRING_BUFFER_LEN, sizeof(char));
if (my_string == NULL) { return 1; }
// Use the allocated space in some way
my_string[0] = 'a';
my_string[1] = 'b';
// Free using the dedicated function
secure_free(&my_string, STRING_BUFFER_LEN);
return 0;
}
Compiling with GCC (Rev6, Built by MSYS2 project, 10.2.0):
$ gcc securefree.c -o securefree
securefree.c: In function 'main':
securefree.c:29:17: warning: passing argument 1 of 'secure_free' from incompatible pointer type [-Wincompatible-pointer-types]
29 | secure_free(&my_string, STRING_BUFFER_LEN);
| ^~~~~~~~~~
| |
| char **
securefree.c:11:32: note: expected 'void **' but argument is of type 'char **'
11 | static void secure_free(void** p_p_data, size_t length_in_bytes)
| ~~~~~~~^~~~~~~~
EDIT: the macro version looks like this
#define secure_free_macro(ptr, len) if ((ptr) != NULL) { \
memset((ptr), 0, (len)); free(ptr); (ptr) = NULL; }