How to Debug Heap Corruption Errors

How to debug heap corruption errors?

Application Verifier combined with Debugging Tools for Windows is an amazing setup. You can get both as a part of the Windows Driver Kit or the lighter Windows SDK. (Found out about Application Verifier when researching an earlier question about a heap corruption issue.) I've used BoundsChecker and Insure++ (mentioned in other answers) in the past too, although I was surprised how much functionality was in Application Verifier.

Electric Fence (aka "efence"), dmalloc, valgrind, and so forth are all worth mentioning, but most of these are much easier to get running under *nix than Windows. Valgrind is ridiculously flexible: I've debugged large server software with many heap issues using it.

When all else fails, you can provide your own global operator new/delete and malloc/calloc/realloc overloads -- how to do so will vary a bit depending on compiler and platform -- and this will be a bit of an investment -- but it may pay off over the long run. The desirable feature list should look familiar from dmalloc and electricfence, and the surprisingly excellent book Writing Solid Code:

  • sentry values: allow a little more space before and after each alloc, respecting maximum alignment requirement; fill with magic numbers (helps catch buffer overflows and underflows, and the occasional "wild" pointer)
  • alloc fill: fill new allocations with a magic non-0 value -- Visual C++ will already do this for you in Debug builds (helps catch use of uninitialized vars)
  • free fill: fill in freed memory with a magic non-0 value, designed to trigger a segfault if it's dereferenced in most cases (helps catch dangling pointers)
  • delayed free: don't return freed memory to the heap for a while, keep it free filled but not available (helps catch more dangling pointers, catches proximate double-frees)
  • tracking: being able to record where an allocation was made can sometimes be useful

Note that in our local homebrew system (for an embedded target) we keep the tracking separate from most of the other stuff, because the run-time overhead is much higher.


If you're interested in more reasons to overload these allocation functions/operators, take a look at my answer to "Any reason to overload global operator new and delete?"; shameless self-promotion aside, it lists other techniques that are helpful in tracking heap corruption errors, as well as other applicable tools.


Because I keep finding my own answer here when searching for alloc/free/fence values MS uses, here's another answer that covers Microsoft dbgheap fill values.

How do I diagnose heap corruption errors on Windows?

Use the debug heap and call this at the very beginning in main().

_CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF);

It will slow down the program a lot but it should break as soon as corruption occurs.

Refer to this article for details: https://msdn.microsoft.com/en-us/library/974tc9t1.aspx#BKMK_Check_for_heap_integrity_and_memory_leaks

How do I debug my heap corruption? (C++)

The issue is this section of code:

for (int i = 0, j = 0; i < nOperatorCount; i++)
{
if (pnOperatorType[i] == 2)
{
pnNewExpressions[j] = pnExpressions[i] * pnExpressions[i + 1]; // <<--- Error

The subscript j is out of bounds in the loop for the input of 2*3*4+2*3+2.

But here is the reason why I found the error quickly -- I replaced all of those calls to new[] and delete[] with std::vector. Since you're using Visual Studio (the error is a VS error), the DEBUG version does automatic checking for vector array bounds errors. The error box that shows up gives you a subscript out of range error, and thus the VS IDE goes right to the line where the violation occurs.

Now, the std::vector::operator[] does not check bounds in a release version, and in general operator [] is not supposed to do so. However the debugging libraries for VS turns on this check. So you can say I am taking advantage of what Visual Studio offers in terms of debugging help. But I was only afforded this once I changed to using std::vector.

The vector::at() function serves the same purpose as using [], but it gives you the range checking, regardless of whether you are running a release or debug version. I didn't replace the calls with at() here, but if for some reason [] didn't find the error, I could always change the [] to at() calls in the code until I get an error generated.

Here is a quick synopsis of the changes:

#include <iostream>
#include <string>
#include <vector>
//...
std::vector<int> pnOperatorLocation(nOperatorCount + 1);
std::vector<int> pnOperatorType(nOperatorCount);
std::vector<float> pnExpressions(nOperatorCount + 1);
//...
std::vector<float> pnNewExpressions(nOperationsCount);

In addition, all of the calls to delete[] are no longer necessary.

Does this fix the error? No. I didn't want to check your algorithm/logic in trying to solve the equation. You still need to put in that effort to figure out what the purpose of that loop is, and why j goes out of bounds. However it does show that merely using modern C++ techniques can be advantageous. Using new[] and delete[] to create dynamic arrays is "old fashioned" and really not necessary, unless you really do need to use it.

Basically, you need to ask yourself -- are you trying to code an equation solver? If the answer is "yes", then use the tools available to create one (such as std::vector) and move on with solving "easy" bugs, instead of fighting with using pointers and putting in a lot of effort in maintaining dynamic arrays correctly.

How to detect heap corruption errors under MinGW?

There is a tool provided by Microsoft called Application Verifier. It is a gui tool that changes system settings to run selected applications in a controlled environment. This makes it possible to crash your program if it causes detectable memory errors. This is a controlled crash that can be debugged.

Fortunately it is obtainable from Microsoft as a separate download. Another way to get it is to have Windows SDK installed with checked Application Verifier checkbox. SDK offers also an option Application Verifier redistributable.

After you configure Application Verifier to have an eye for your app, you need to debug it. Debugging under MinGW is a more common subject, already explained on stackoverflow. [mingw] [debugging] query on stackoverflow gives interesting articles. One of them is How do I use the MinGW gdb debugger to debug a C++ program in Windows?. Gdb is the one I used.

The general questions How to debug heap corruption errors? and Heap corruption detection tool for C++ were helpful to find this tool, but I wasn't sure if it is compatible with MinGW. It is.

Heap Corruption Detected in Visual Studio, but program runs fine in another C compiler: why?

If you are doing something that is undefined behaviour (dynamic memory allocation is a real hot-bed of these issues), one of the undefined things that may happen is that it will work just fine.

That by no means indicates that you're doing the right thing, UB is something that should be avoided since it may act differently on another system, another compiler, or even next Tuesday at 3:05 pm :-)


A couple of things I will mention:

  • you don't need all those function declarations (the prototypes) immeditely before the definitions. A definition acts as a declaration if it hasn't already been declared.
  • your exist() function is likely to crash for an empty list since it dereferences top. If your intent is to detect a non-empty list, you can just use return (top != NULL);.
  • you shouldn't explicitly cast the return value of malloc in C, it can cause certain subtle errors.

And, in fact, while looking at that last bullet point, there's your error (not specifically to do with casting, just that it's on that particular line):

struct Node* add = (struct Node*)malloc(sizeof(struct Node*));
// ^
// oops!

The size of struct Node* is the size of a pointer to your structure, commonly (but not necessarily) four or eight octets. Given your actual structure has an integer and a pointer, that size is not going to be big enough.

It may work on some systems that provide a minimum dynamic memory allocation but it's definitely not advised. You should be doing:

struct Node *add = malloc(sizeof(struct Node));
// ^^^^^^^^^^^
// size of the node, not pointer

That line has both the cast removed, and the correct size.

How to find heap corruption in my DLL with Application Verifier?

Why not write a simple wrapper app? I've had to do that for a third party DLL that caused heap corruptions that would later crash our app. In this case it was in the destructor so I only had to load and free it. But if that didn't find it for you you could add unit tests for each export.

how to debug possible heap corruption?

I'd suggest you start by using Application Verifier to test for heap corruption. Application Verifier is a free tool from Microsoft that helps identify resource leaks and heap corruption. You can find App Verifier at url: http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=20028 You can find out how to enable it for heap corruption and other things from http://technet.microsoft.com/en-us/library/bb457063.aspx

It is often useful in these situations.



Related Topics



Leave a reply



Submit