Memory corruption from double free of heap pointer in native code

Critical Risk memory-safety
ccppmemory-safetydouble-freeheap-corruptionuse-after-freememory-managementrce

What it is

Double free vulnerabilities occur when the same dynamically allocated memory pointer is freed twice without being reset or having clear ownership. This can corrupt the heap allocator's internal structures, leading to crashes, memory corruption, and potentially enabling remote code execution through heap exploitation techniques.

/* VULNERABLE: Double free in error handling */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// VULNERABLE: Function with multiple free paths
int vulnerable_process_data(const char* input) {
    char* buffer = malloc(1024);
    char* temp_buffer = malloc(512);
    
    if (!buffer || !temp_buffer) {
        // VULNERABLE: Free without checking if allocation succeeded
        free(buffer);
        free(temp_buffer);
        return -1;
    }
    
    // Process data
    if (strlen(input) > 1000) {
        printf("Input too long\n");
        free(buffer);
        free(temp_buffer);
        return -1;
    }
    
    strcpy(buffer, input);
    
    // Some processing that might fail
    if (process_internal(buffer, temp_buffer) < 0) {
        free(buffer);
        free(temp_buffer);
        return -1;
    }
    
    // VULNERABLE: Normal path also frees
    free(buffer);
    free(temp_buffer);
    return 0;
    
    // VULNERABLE: If process_internal calls free() internally,
    // this creates a double-free condition
}
/* SECURE: Safe memory management patterns */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

// SECURE: Safe wrapper for free()
void safe_free(void** ptr) {
    if (ptr && *ptr) {
        free(*ptr);
        *ptr = NULL;  // Prevent double-free
    }
}

// SECURE: Macro for safer memory management
#define SAFE_FREE(ptr) do { \
    if (ptr) { \
        free(ptr); \
        ptr = NULL; \
    } \
} while(0)

// SECURE: Function with proper cleanup
int secure_process_data(const char* input) {
    char* buffer = NULL;
    char* temp_buffer = NULL;
    int result = -1;
    
    // Check input parameters
    if (!input) {
        return -1;
    }
    
    // Check input length before allocation
    if (strlen(input) > 1000) {
        printf("Input too long\n");
        return -1;
    }
    
    // Allocate memory
    buffer = malloc(1024);
    temp_buffer = malloc(512);
    
    if (!buffer || !temp_buffer) {
        goto cleanup;  // Safe cleanup handles NULL pointers
    }
    
    // Initialize memory
    memset(buffer, 0, 1024);
    memset(temp_buffer, 0, 512);
    
    // Safe string copy
    strncpy(buffer, input, 1023);
    buffer[1023] = '\0';
    
    // Process data with error checking
    if (secure_process_internal(buffer, temp_buffer) < 0) {
        goto cleanup;
    }
    
    result = 0;  // Success
    
cleanup:
    // SECURE: Single cleanup point, safe for NULL pointers
    SAFE_FREE(buffer);
    SAFE_FREE(temp_buffer);
    return result;
}

💡 Why This Fix Works

The vulnerable example shows common double-free scenarios including unclear ownership and multiple cleanup paths. The secure alternative implements comprehensive memory safety patterns including NULL pointer checks, single cleanup points, and safe free macros to prevent double-free conditions.

Why it happens

Multiple parts of the code believe they own the same memory allocation, leading to multiple calls to free() on the same pointer without coordination or ownership tracking.

Root causes

Unclear Memory Ownership

Multiple parts of the code believe they own the same memory allocation, leading to multiple calls to free() on the same pointer without coordination or ownership tracking.

Exception Handling Issues

Error handling paths and exception cleanup code may free memory that has already been freed in the normal execution path, especially in complex control flow scenarios.

Dangling Pointer Reuse

Pointers are not set to NULL after being freed, allowing subsequent code to accidentally attempt to free the same memory location again.

Fixes

1

Implement Safe Memory Management

Always set pointers to NULL immediately after freeing them, and check for NULL before attempting to free. Establish clear ownership rules for memory allocations.

2

Use Memory Safety Tools

Deploy AddressSanitizer (ASan), Valgrind, or other memory debugging tools during development and testing to detect double-free conditions before deployment.

3

Adopt Modern Memory Management

Consider using smart pointers in C++, reference counting, or garbage collection where appropriate. Implement RAII patterns to tie memory lifetime to object scope.

Detect This Vulnerability in Your Code

Sourcery automatically identifies memory corruption from double free of heap pointer in native code and many other security issues in your codebase.