Avoiding Double Free

Last time we talked about what happens when you use memory after freeing it. Today, let's look at another common memory pitfall that's just as dangerous: freeing the same memory twice. This is called a double free, and it's a direct path to undefined behavior and serious security vulnerabilities.

Let's dive into what it is, why it's a problem, and how modern C++ helps us avoid it completely.


What is a Double Free?

A double free vulnerability occurs when a program calls delete (or free()) more than once on the same memory address. After the first delete, the memory is returned to the system's memory manager. The pointer itself, however, still holds that address, becoming a dangling pointer.

When you call delete on that same pointer again, you're telling the memory manager to release a resource that it may have already re-allocated for a completely different purpose. This can corrupt the memory manager's internal data structures, leading to unpredictable crashes or, in a worst-case scenario, creating an exploitable condition.

A Example of Vulnerable Code

Here’s a simple scenario that illustrates how a double free can happen. Imagine a Player object that gets passed around different parts of a game system.

Article content

In this code, both p1 and p2 point to the same Player object. The first delete p1; correctly destroys the object and returns its memory. The second delete p2; attempts to destroy an object that no longer exists at a memory address that the program no longer owns. This is where things go wrong. An attacker might be able to manipulate the program's memory between the two delete calls to gain control of the application.


How to Avoid Double Free Vulnerabilities

Just like with use-after-free, the key to preventing double free errors is to have clear, unambiguous ownership semantics. Modern C++ provides the perfect tools for this.

1. Establish Clear, Exclusive Ownership with std::unique_ptr

If an object should only have one owner, std::unique_ptr is the perfect choice. It ensures that only one pointer can own and delete the memory. You simply cannot copy a std::unique_ptr, which makes accidental double-deletes impossible at compile time.

Article content

2. Manage Shared Ownership with std::shared_ptr

For situations where multiple parts of your program legitimately need to share ownership of an object, std::shared_ptr is the answer. It uses a reference count to track how many shared_ptr instances are pointing to an object. The object is only deleted when the very last shared_ptr to it is destroyed.

This elegantly solves our original problem:

Article content

Final Thoughts

Double free errors stem from confusion over who is responsible for cleaning up memory. By using smart pointers, we bake ownership rules directly into our types. This lets the compiler enforce memory safety for us, eliminating an entire category of dangerous bugs. Ditch the new and delete calls in your application logic and embrace the safety and clarity of smart pointers.

Happy, safe coding!

To view or add a comment, sign in

More articles by Pawan Wagh

  • Running Open-Source LLMs on Your Own Machine

    A few years ago, running a large language model on your laptop felt like a weekend experiment: fragile, slow, and…

  • False Sharing and Cache Line Contention

    Your multithreaded code is slow, and you have no idea why. You profiled it.

  • 2025 Computing Recap: Chips, Quantum, Models, and What's Next

    Last year was a turning point for computing systems. We saw breakthroughs that were theoretical for decades finally…

    1 Comment
  • Memory Barriers and CPU Reordering

    Your CPU is lying to you. Not maliciously, but the instructions you write aren't executing in the order you wrote them.

    4 Comments
  • The Real Cost of Virtual Functions: A Performance Deep Dive

    Virtual functions are everywhere in modern C++ codebases. We use them without thinking twice because they give us…

  • Why Hardware is the New Frontier of Memory Safety

    For the last twenty years, the software industry has been fighting a losing battle against memory safety…

    2 Comments
  • Heap Fragmentation and Custom Allocators

    Welcome to the 20th Edition of The Dangling Pointer! Heap fragmentation is a persistent challenge in systems…

  • Building Your First MCP Client

    If you've been hearing about the Model Context Protocol and wondering how to actually build a client that talks to MCP…

    1 Comment
  • Build an MCP server

    When you use an AI model, it's often in a sandbox, disconnected from your other applications and data. If you want the…

  • Intro to MCP : Part 2 (Interactions)

    MCP has 3 core participants: Host, Client and Server Host - Manage one or multiple MCP clients Client - Manage…

    1 Comment

Others also viewed

Explore content categories