Mismatched Memory Management Routines
Description
Mismatched Memory Management Routines is a memory safety vulnerability where software attempts to return memory to the system using a deallocation function that is incompatible with the function originally used to allocate that memory. Common examples include freeing stack-allocated memory with free(), memory allocated with malloc() deallocated with delete, memory allocated with new deallocated with free(), and array memory allocated with new[] but deallocated with scalar delete instead of delete[]. These mismatches cause undefined behavior because different allocation mechanisms use different internal data structures and heap layouts.
Risk
Mismatched memory management routines cause heap corruption and undefined behavior. The consequences depend on implementation details but can include crashes, memory corruption, and potentially arbitrary code execution. Different allocators track metadata differently—mixing them corrupts this metadata. For example, new may add object metadata that free() doesn't expect, causing it to misinterpret memory boundaries. The C++ new[]/delete mismatch is particularly dangerous because delete[] needs array size information that scalar delete doesn't process. These bugs may not crash immediately, making them difficult to debug and potentially creating exploitable windows.
Solution
Use only matching pairs of allocation and deallocation functions: malloc/calloc/realloc with free(), new with delete, new[] with delete[]. In C++, prefer smart pointers (unique_ptr, shared_ptr) that automatically use the correct deallocation. Use RAII (Resource Acquisition Is Initialization) patterns. Consider using languages with automatic memory management. Enable runtime detection tools like AddressSanitizer or Valgrind during development. When mixing C and C++ code, be especially careful about which allocation system each component uses.
Common Consequences
| Impact | Details |
|---|---|
| Integrity | Scope: Integrity Modify Memory - Mismatched routines corrupt heap metadata, potentially allowing memory modification. |
| Availability | Scope: Availability DoS: Crash, Exit, or Restart - Heap corruption typically causes crashes. |
| Confidentiality | Scope: Confidentiality, Integrity, Availability Execute Unauthorized Code or Commands - Heap corruption may be exploitable for code execution. |
Example Code
Vulnerable Code
// Vulnerable: new allocated, free() deallocated
void vulnerable_new_free() {
BarObj* ptr = new BarObj();
// ... use object ...
// Vulnerable: Should use delete, not free()
free(ptr); // Heap corruption!
// free() doesn't know about C++ object metadata
// Destructor not called
}
// Vulnerable: malloc allocated, delete deallocated
void vulnerable_malloc_delete() {
int* ptr = (int*)malloc(sizeof(int));
// ... use memory ...
// Vulnerable: Should use free(), not delete
delete ptr; // Undefined behavior
// delete may try to call destructor on non-object
}
// Vulnerable: new[] allocated, delete deallocated (missing [])
void vulnerable_array_delete() {
int* array = new int[100];
// ... use array ...
// Vulnerable: Should use delete[], not delete
delete array; // Only deletes first element!
// Memory leak and potential corruption
// For objects: only first destructor called
}
// Vulnerable: Stack memory freed
void vulnerable_stack_free() {
int stackArray[10];
// ... use array ...
// Vulnerable: Stack memory cannot be freed!
free(stackArray); // Catastrophic heap corruption
}
// Vulnerable: Conditional allocation with mismatched deallocation
void vulnerable_conditional(bool useHeap) {
int localArray[2] = {11, 22};
int* p = localArray; // Points to stack
if (useHeap) {
p = new int[2]; // Now points to heap
}
// ... use p ...
// Vulnerable: delete[] used regardless of allocation type
delete[] p; // Crashes if useHeap is false!
}
// Vulnerable: C code mixing allocators
#include <stdlib.h>
// Custom allocator that wraps malloc
void* my_alloc(size_t size) {
void* ptr = malloc(size + sizeof(size_t));
*(size_t*)ptr = size; // Store size at start
return (char*)ptr + sizeof(size_t);
}
void vulnerable_custom_allocator() {
void* ptr = my_alloc(100);
// ... use memory ...
// Vulnerable: free() doesn't know about size header
free(ptr); // Freeing wrong address!
// Should use matching my_free() function
}
// Vulnerable: Mixing aligned and regular allocation
void vulnerable_aligned() {
void* ptr = aligned_alloc(64, 1024); // Aligned allocation
// ... use memory ...
// May be vulnerable depending on implementation
// Some systems require special deallocation for aligned memory
free(ptr); // May or may not be correct
}
// Vulnerable: Class with mismatched allocator
class VulnerableClass {
private:
char* buffer;
public:
VulnerableClass(size_t size) {
// Allocate with malloc
buffer = (char*)malloc(size);
}
~VulnerableClass() {
// Vulnerable: Deallocate with delete[]
delete[] buffer; // Mismatch!
}
};
// Vulnerable: Returning allocated memory with wrong type
char* vulnerable_return_buffer() {
// Allocated with new[]
char* buf = new char[256];
return buf;
}
void use_vulnerable() {
char* str = vulnerable_return_buffer();
// Caller doesn't know how buffer was allocated
free(str); // Vulnerable if they guess wrong
}
Fixed Code
// Fixed: Matching new/delete
void fixed_new_delete() {
BarObj* ptr = new BarObj();
// ... use object ...
// Fixed: Matching delete for new
delete ptr; // Correct: destructor called, memory freed properly
}
// Fixed: Matching malloc/free
void fixed_malloc_free() {
int* ptr = (int*)malloc(sizeof(int));
if (ptr == NULL) return;
// ... use memory ...
// Fixed: Matching free for malloc
free(ptr); // Correct
}
// Fixed: Matching new[]/delete[]
void fixed_array_delete() {
int* array = new int[100];
// ... use array ...
// Fixed: Matching delete[] for new[]
delete[] array; // Correct: all elements handled
}
// Fixed: Don't free stack memory
void fixed_stack_memory() {
int stackArray[10];
// ... use array ...
// Fixed: Stack memory automatically reclaimed when function returns
// No deallocation needed
}
// Fixed: Track allocation type
void fixed_conditional(bool useHeap) {
int localArray[2] = {11, 22};
int* p = localArray;
if (useHeap) {
p = new int[2];
}
// ... use p ...
// Fixed: Only delete if we allocated
if (useHeap) {
delete[] p;
}
// Stack memory doesn't need deallocation
}
// Fixed: Use smart pointers
#include <memory>
void fixed_smart_pointer() {
// unique_ptr automatically uses delete
std::unique_ptr<BarObj> ptr = std::make_unique<BarObj>();
// ... use object via ptr.get() or *ptr ...
// Automatic cleanup with correct delete when ptr goes out of scope
}
void fixed_array_smart_pointer() {
// unique_ptr for arrays automatically uses delete[]
std::unique_ptr<int[]> array = std::make_unique<int[]>(100);
// ... use array via array[i] or array.get() ...
// Automatic cleanup with correct delete[]
}
// Fixed: Consistent allocator wrapper
class SecureBuffer {
private:
char* buffer;
size_t size;
public:
SecureBuffer(size_t s) : size(s) {
buffer = new char[size]; // Consistent: use new[]
}
~SecureBuffer() {
delete[] buffer; // Consistent: use delete[]
}
// Prevent copying to avoid double-free
SecureBuffer(const SecureBuffer&) = delete;
SecureBuffer& operator=(const SecureBuffer&) = delete;
char* data() { return buffer; }
size_t length() { return size; }
};
// Even better: use std::vector
#include <vector>
void fixed_with_vector() {
std::vector<char> buffer(256);
// ... use buffer.data() ...
// Automatic proper deallocation
}
// Fixed: Custom allocator with matching free
#include <stdlib.h>
void* my_alloc(size_t size) {
void* ptr = malloc(size + sizeof(size_t));
if (ptr == NULL) return NULL;
*(size_t*)ptr = size;
return (char*)ptr + sizeof(size_t);
}
// Fixed: Matching deallocation function
void my_free(void* ptr) {
if (ptr == NULL) return;
// Adjust pointer back to real start
void* real_ptr = (char*)ptr - sizeof(size_t);
free(real_ptr);
}
void fixed_custom_allocator() {
void* ptr = my_alloc(100);
if (ptr == NULL) return;
// ... use memory ...
// Fixed: Use matching deallocator
my_free(ptr);
}
CVE Examples
- CVE-2006-1564: Mismatched memory management routines in media player caused exploitable crash.
- CVE-2015-0821: Use of delete instead of delete[] caused memory corruption.
References
- MITRE Corporation. "CWE-762: Mismatched Memory Management Routines." https://cwe.mitre.org/data/definitions/762.html
- CERT C++ Coding Standard. "MEM51-CPP. Properly deallocate dynamically allocated resources."
- C++ Core Guidelines. "R.11: Avoid calling new and delete explicitly."