• Browse topics
Login
Sign up
Login

SNYK LEARN LOGIN

OTHER REGIONS

For Snyk Enterprise customers with regional contracts. More info

Sign up

Double free

Free your memory but be careful

C++

Double free: the basics

What is double free?

Double free is a memory management flaw that occurs when a program releases the same memory block twice using the free() or delete function. Memory unsafe languages such as C++ are particularly prone to this vulnerability.

Calling the free() function with the same argument more than once can result in corrupted memory management data structures. This can lead to all kinds of other unexpected behavior, which could include arbitrary code execution and program crashes.

About this lesson

In this lesson, you will learn about vulnerabilities stemming from double free and how to protect your applications against them. We will step into the shoes of a data analyst who happens to stumble upon a double free bug in the wild. After this scenario, we will go over the vulnerable code, the impact, and some possible mitigations.

FUN FACT

Counting bytes

Did you know that in the early days of computer programming, memory was so scarce and expensive that programmers had to optimize their code to use as little memory as possible? They would sometimes even count individual bytes to ensure efficiency!

Double free in action

Testing for double free

  • STEP 1
  • STEP 2
  • STEP 3
  • STEP 4
  • STEP 5
  • STEP 6

Setting the stage

Your latest project involves analyzing server logs. A new service called Loggerr promises to process logs and yield insights instantly. Let's test it.

double-free-1.svg

Double free under the hood

Let's break down what happened in the story above.

We were sending requests to the /action endpoint to perform log analysis. At some point, we sent a concatenated version of two actions, and the server seemed to have crashed.

A simplified version of the backend code from Loggerr is the following:

The endpoint receives a string parameter, which holds the action the user wants to execute on the log file. If analyze_trends is present in the action, then the analyzeTrends function will be called, and its response will be returned with activity_heatmap and error_rate.

At first glance, there seems to be nothing particularly wrong with this code. The log_data variable is freed after it has been used by an action. But notice how there are multiple free(log_data) calls. Somehow we were able to trigger multiple of those by supplying an action string that contained two or more action names.

We were able to trigger code behavior that the developer had not foreseen. Because the action variable would contain multiple different action names, each of those action.find() calls would be true, and the application would perform multiple actions, ending with multiple free() calls on log_data.

What is the impact of double free?

Double freeing memory can introduce significant risks. When memory is released using free(), the associated block becomes available for other allocations within the program. Freeing the same memory block again could lead to the corruption of currently allocated memory or of the internal structures used by the memory management system.

From a security perspective, double free vulnerabilities can be quite concerning. Attackers with knowledge of such vulnerabilities might exploit them to overwrite function pointers, return addresses, or other crucial data. Successfully manipulating these structures can grant the attacker the ability to run arbitrary code or elevate privileges within the application or system.

In our situation with Loggerr, the crash occurred because of the underlying memory management mechanisms in place. Modern glibc (GNU C Library) contains built-in checks that detect anomalies like double frees. Upon detection, instead of proceeding with potentially corrupted memory operations, the system halts, ensuring that the application's state remains uncompromised.

Scan your code & stay secure with Snyk - for FREE!

Did you know you can use Snyk for free to verify that your code
doesn't include this or other vulnerabilities?

Scan your code

Double free mitigation

Preventing a double free vulnerability can be as simple as making sure you don’t double free a memory block. But this is easier said than done, as demonstrated in the scenario above.

The backend code of Loggerr can be changed to the following:

The free(log_data) call has been moved to the end of the function, making sure that it's only called once. Additionally, the separate if statements have been combined into if else statements, to counter the possibility of executing multiple actions.

Apart from preventing double free, you can also mitigate it in several ways. As you might have noticed, the code above sets the log_data variable to NULL after freeing the memory it points to. This is a safeguard against dangling pointer pitfalls.

When you access a dangling pointer after it has been freed, there's a risk of reading or modifying unintended sections of memory. However, if you try to access a nullified pointer, most systems will immediately crash. This crash provides instant feedback about the problem, making debugging more straightforward and exploitation practically impossible.

In addition to the code safeguards we've discussed, we also have underlying system protections at play. Remember when we ran into that crash with Loggerr? That intervention came courtesy of the glibc (GNU C Library). Modern glibc versions have built-in checks specifically designed to catch issues like double frees. When they detect such anomalies, they immediately intervene, stopping potential memory corruption in its tracks.

FUN FACT

Smart pointers

In C++, there's a feature called smart pointers – specifically std::unique_ptr and std::shared_ptr. These pointers automatically manage memory for you, eliminating issues like double freeing. It's a seamless way to handle memory without the usual manual overhead.

Keep learning

Learn more about double free with some additional resources