Preventing Dangling Pointers in C

An approach to preventing dangling pointers in C using specialized pointers that automatically reset to NULL once the referenced objects are destroyed.

Contents

The Problem

Dangling pointers are a common issue in C programming, especially during runtime memory allocation and deallocation. The following code illustrates this: The variable s accesses memory that has already been released via the standard library’s free function.

#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 from or writing to memory using dangling pointers can cause severe security and stability issues. To prevent potentially dangerous memory access, it is standard practice to set pointer variables to NULL after calling free. However, free is often invoked in a different part of the codebase than where the objects are used, or even within a library. Consequently, it may be unknown whether the object a pointer references still exists.

The Solution

The xptr concept solves the dangling pointer problem by introducing specialized pointers that can be automatically set to NULL by the code responsible for memory management. The following example demonstrates how to use 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;

While xptrs mitigate dangling pointers, implementing them requires modifying existing code to call xptr_null after free. Another approach is hooking the standard free function and replacing it with a wrapper that executes both the original free and xptr_null.

The xptr type is defined as void *. To ensure safety, pointers to target memory addresses are encapsulated within the xptr system, which only exposes pointers to internal storage. Resetting an xptr to NULL affects only the xptr internal memory and never touches external pointer variables.

Downloads

This proof of concept implementation is not suitable for production. It stores xptrs in a simple array, which should be replaced by an efficient data structure like a hash table for production use. Additionally, this implementation lacks thread safety.

Project xptr.zip

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