Preventing Dangling Pointers in C Using Pointers to Pointers

Dangling pointers pose a problem in programs developed with the C programming language. The problem is of particular importance when memory is allocated and freed at runtime. The code below demonstrates the problem of dangling pointers. The variable s is used to access memory after it has been freed by calling the free function of the C standard library:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char *const s = malloc(sizeof(char) * 20);
    if (s == NULL)
        return EXIT_FAILURE;
    strcpy(s, "Hello World!");
    printf("%s\n", s);

    // Free memory used to store the string.
    free(s);

    // Later in the code:
    // Variable `s` is a dangling pointer.
    printf("%s\n", s);
    return EXIT_SUCCESS;
}

Reading or writing memory using dangling pointers can cause severe security and stability issues. To prevent the potentially dangerous access to memory via a dangling pointer, it is generally good practice to set pointer variables to NULL after calling free. However, often free is called in an another part of the code than the places where the objects are used, or even inside a library. Sometimes it is unknown if the object pointed to by a pointer still exists.

The presented concept of xptr solves the problem of dangling pointers by introducing special pointers that can be set to NULL by the code that is responsible for allocation and freeing of memory. The code below demonstrates the application of xptrs:


// Library code:  Allocation of memory used to store the object.
object *lib_po;
lib_po = malloc(…);

// Program code:  Obtaining a pointer to the object created by the library.
object *program_po = lib_get_object();

// Program code:  Creating an xptr to the object.
xptr *program_xpo = xptr_create(program_po);

// Library code:  Memory used to store the object is freed and all xptrs
// pointing to it are set to `NULL` by calling `xptr_null`.
free(lib_po);
xptr_null(lib_po);
lib_po = NULL;

// Program code:  Accessing the object via the dangling pointer could fail.
int id = program_po->id;   // Failure.

// Program code:  Accessing the object via the xptr.
// Dereference the xptr to obtain the target pointer.
object *program_po = *program_xpo;
if (program_po == NULL)
    printf("Object does not exist any more.\n");
else
{
    int id = program_po->id;   // Success.
    printf("Pointer to the object could be obtained successfully.\n");
}

// Program code:  The xptr is not needed any more, free it.
xptr_free(program_xpo);
program_xpo = NULL;

Although xptrs can help mitigate the problem of dangling pointers, they cannot be used without altering existing code to call xptr_null after calling free. Alternatively, the C standard library’s free function can be hooked and replaced by an extended version that calls the original free function followed by a call to xptr_null.

The implementation defines the type xptr as void *. Pointers to the target memory addresses are stored inside the xptr implementation for safety reasons. The present implementation only exposes pointers to the internal storage. Setting an xptr to NULL solely writes to memory belonging to the xptr implementation and never writes to pointer variables stored elsewhere.

A proof of concept implementation of xptr is provided. This implementation is not suitable for use in production code. The xptrs are stored in a simple array. For production code, a suitable efficient data structure, such as a hash table, should be used instead of the array. In addition, the provided implementation is not thread-safe.

Project xptr.zip

C project for Code::Blocks, GNU Make, and Geany.