Introduction
While using Intel® Inspector XE in Visual Studio 2015 to detect memory leaks and stack manipulation issues in your code written in C/C++, you’ve might encountered that Intel® Inspector XE detects mismatched allocation/deallocation issues even though you properly deallocate each buffer after it has been previously allocated. In this article, we’ll discuss about how to properly deallocate memory buffers being allocated to survive memory mismatched allocation/deallocation issues detected by Intel® Inspector XE.
Memory Mismatched Allocation/Deallocation Issues
Suppose you have the following code that allocates a buffer in memory to store an array of N objects of specific type DATA
and assigns address of buffer in memory to the pointer variable data:
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
typedef struct tagData
{
char* string;
} DATA, NEAR *PDATA, FAR* LPDATA;
const size_t N = 10;
int main()
{
int count = 10;
while (--count >= 0)
{
LPDATA data = (LPDATA)malloc(sizeof(DATA) * N);
memset((void*)&data[0], 0x00, sizeof(DATA) * N);
const size_t length = 1024;
for (size_t index = 0; index < N; index++)
{
data[index].string = (char*)malloc(length);
sprintf(data[index].string, "string: %zu", index);
}
for (size_t index = 0; index < N; index++)
free(data[index].string);
free(data);
}
return 0;
}
This is typically done by calling one of those C memory allocation functions such as calloc
, malloc
, realloc
, as well as using new[]
and delete[]
operators in C++. Then, we’re iterating through the array of objects data and for each object we’re allocating process memory to store a character buffer string of size length using those memory allocation functions. In this case, we’re iteratively assigning the address of buffer being allocated to the pointer variable string
of each particular object data[index]
.
Since those memory buffers have been allocated, we can do some work using the buffers being allocated. Finally, according to the best programming practices, to avoid memory leaks, we need to smartly deallocate specific buffers at the end of the code execution. To do this, we iterate through the array of objects, and for each particular object we deallocate memory used to store a string buffer by using C function free(data[index].string)
or delete[] data[index].string
operator in C++. After that, we deallocate buffer used to store an array of objects data using the same free(data) function.
Normally, by executing the following code listed above, we will see that it obviously can be successfully executed providing the correct results with no memory leaks or other issues at the end of its execution. However, by using Intel® Inspector XE to detect and analyze memory leaks or other issues that might persist during the code execution, we might encounter that the following code suddenly incurs a series of mismatched allocation/deallocation problems. We purposely repeat the process of memory allocation/deallocation to create workload on the CPU so that the following problems are easily detected by Intel® Inspector XE. Those problems basically occur when running the following code under Windows kernel:
The main reason why those problems might be incurred by this code and thus detected by the Intel® Inspector XE is that calling the either free(data[index].string)
function or free(data)
does not actually deallocate the buffers when the following code is run under Windows kernel. Calling of these functions is equivalent to assigning the null
-pointer to the specific pointer variables such as (data = nullptr
). In turn, the memory being previously allocated at the beginning of code execution is automatically redistributed by the Windows kernel among other running processes. That’s actually why, Intel® Inspector suddenly detects those issues being discussed in this article.
Workaround
As a workaround to the problem being discussed, we have to use ::VirtualAllocEx(...)
and ::VirtualFreeEx(...)
WinAPI-functions to properly allocate/deallocate memory buffers. Also, to make sure, that we’re actually deallocating memory that was previously allocated, we have to perform a check if the value of address assigned to a pointer variable is not equal to nullptr
. If so, perform deallocation, otherwise do nothing. Memory buffers allocation/deallocation is typically done as shown in the code example below:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <memory.h>
#include <Windows.h>
typedef struct tagData
{
char* string;
} DATA, NEAR *PDATA, FAR* LPDATA;
const size_t N = 10;
int main()
{
int count = 10;
while (--count >= 0)
{
HANDLE hProcHandle = ::GetCurrentProcess();
LPDATA data = (LPDATA)::VirtualAllocEx(hProcHandle, NULL, \
sizeof(DATA) * N, MEM_COMMIT, PAGE_READWRITE);
::ZeroMemory((void*)&data[0], sizeof(DATA) * N);
const size_t length = 1024;
for (size_t index = 0; index < N; index++)
{
data[index].string = (char*)::VirtualAllocEx(hProcHandle, NULL, \
length, MEM_COMMIT, PAGE_READWRITE);
sprintf(data[index].string, "string: %zu", index);
}
for (size_t index = 0; index < N; index++)
if (data[index].string != NULL)
::VirtualFreeEx(hProcHandle, data[index].string, length, MEM_RELEASE);
if (data != NULL)
::VirtualFreeEx(hProcHandle, data, sizeof(DATA) * N, MEM_RELEASE);
}
return 0;
}
In this case, unlike using malloc(…)
and free(…)
functions, ::VirtualAllocEx(...)
and ::VirtualFreeEx(…)
WinAPI - functions allocate/deallocate specific buffers in Windows kernel virtual memory address space. Since that, Intel® Inspector XE no longer detects any problems such as the memory allocation/deallocation mismatch. The detailed description of those functions that perform virtual memory management can be found here.
History
- May 1, 2017 - The first revision of this tip/trick was published