How to debug heap corruption errors?
Asked Answered
S

15

177

I am debugging a (native) multi-threaded C++ application under Visual Studio 2008. On seemingly random occasions, I get a "Windows has triggered a break point..." error with a note that this might be due to a corruption in the heap. These errors won't always crash the application right away, although it is likely to crash short after.

The big problem with these errors is that they pop up only after the corruption has actually taken place, which makes them very hard to track and debug, especially on a multi-threaded application.

  • What sort of things can cause these errors?

  • How do I debug them?

Tips, tools, methods, enlightments... are welcome.

Spates answered 18/6, 2009 at 0:1 Comment(0)
O
137

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.

Outcry answered 18/6, 2009 at 4:46 Comment(6)
One tiny thing worth noting about Application Verifier: you must register Application Verifier's symbols ahead of the microsoft symbol server symbols in your symbol search path, if you use that... Took me a bit of searching to figure out why !avrf wasn't finding the symbols it needed.Outcry
Application Verifier was a great deal of help, and combined with some guessing, I was able to solve the problem! Thanks a lot, and for everyone else too, for bringing up helpful points.Spates
Does Application Verifier have to be used with WinDbg, or should it work with the Visual Studio debugger? I've been trying to use it, but it doesn't raise any errors or apparently do anything when I debug in VS2012.Afc
@NathanReed: I believe it works with VS as well -- see msdn.microsoft.com/en-us/library/ms220944(v=vs.90).aspx -- although note this link is for VS2008, I'm not sure about later versions. Memory is a bit fuzzy, but I believe when I had the issue in the "earlier question" link I just ran Application Verifier and saved the options, ran the program, and when it crashed I chose VS to debug with. AV just made it crash / assert earlier. The !avrf command is specific to WinDbg as far as I know, though. Hopefully others can provide more info!Outcry
Thanks. I actually solved my original issue and it turned out not to be heap corruption after all, but something else, so that probably explains why App Verifier didn't find anything. :)Afc
Just found a heap corruption I've been chasing for days, which I finally caught not by any of the tools mentioned here. In my case, I got a crash while deleting some memory upon destruction. I've noticed that the "heap stamp" was being overwritten (I'm referring to the 4 bytes before the actual allocated memory). By placing a memory breakpoint on those 4 bytes, I found what was causing the corruption - it was an "array[-1] = value", where "array" was the allocated memory, and the "-1" came from a member with a bad value. So when this scenario occurs, this method is the fastest.Coalition
B
39

You can detect a lot of heap corruption problems by enabling Page Heap for your application . To do this you need to use gflags.exe that comes as a part of Debugging Tools For Windows

Run Gflags.exe and in the Image file options for your executable, check "Enable Page Heap" option.

Now restart your exe and attach to a debugger. With Page Heap enabled, the application will break into debugger whenever any heap corruption occurs.

Basidium answered 18/6, 2009 at 4:24 Comment(4)
yes but once i get this function call in my callstack dump (after memory corruption crash) : wow64!Wow64NotifyDebugger , what I can I do ? I still don't know what is going wrong in my applicationStegman
Just tried out gflags to debug heap corruption here, VERY useful little tool, highly recommended. Turned out I was accessing freed memory, which, when instrumented with gflags will immediately break into the debugger... Handy!Kriemhild
Great Tool! Just found a bug, that I was hunting for days, because Windows doesn't say the address of the corruption, only that "something" is wrong, which is not really helpfull.Fiscus
A bit late to the party, but I noticed a significant increase memory usage my the application I am debugging when I turned on Page Heap. Unfortunately up to the point the (32bit) application runs out of memory before the heap corruption detection is triggered. Any ideas how to tackle that problem?Westfalen
M
15

To really slow things down and perform a lot of runtime checking, try adding the following at the top of your main() or equivalent in Microsoft Visual Studio C++

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );
Malvina answered 13/1, 2012 at 23:37 Comment(1)
While that made things really slow for me, I instead put calls to _CrtCheckMemory() before and after some places in my code which I suspected of causing the issue. A bit like lying "mouse traps" to better pinpoint the location at which the error occurs.Criseldacrisey
P
14

A very relevant article is Debugging Heap corruption with Application Verifier and Debugdiag.

Parkinson answered 6/8, 2009 at 9:17 Comment(0)
S
10

One quick tip, that I got from Detecting access to freed memory is this:

If you want to locate the error quickly, without checking every statement that accesses the memory block, you can set the memory pointer to an invalid value after freeing the block:

#ifdef _DEBUG // detect the access to freed memory
#undef free
#define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666;
#endif
Symptomatology answered 18/6, 2009 at 8:54 Comment(0)
L
9

What sort of things can cause these errors?

Doing naughty things with memory, e.g. writing after the end of a buffer, or writing to a buffer after it's been freed back to the heap.

How do I debug them?

Use an instrument which adds automated bounds-checking to your executable: i.e. valgrind on Unix, or a tool like BoundsChecker (Wikipedia suggests also Purify and Insure++) on Windows.

Beware that these will slow your application, so they may be unusable if yours is a soft-real-time application.

Another possible debugging aid/tool might be MicroQuill's HeapAgent.

Lustreware answered 18/6, 2009 at 0:9 Comment(3)
Rebuilding the application with debugging runtime (/MDd or /MTd flag) would be my first step. These perform additional checks at malloc and free, and are often quit effective at narrowing down the location of the bug(s).Sweptback
MicroQuill's HeapAgent: There's not much written or heard about it, but for heap corruption, it should be on your list.Hives
BoundsChecker works fine as a smoke test, but don't even think about running a program under it while trying to run that program in production as well. The slowdown can be anywhere from 60x to 300x, depending on which options you are using, and whether or not you are using the compiler instrumentation feature. Disclaimer: I am one of the guys who maintains the product for Micro Focus.Rosenzweig
I
7

The best tool I found useful and worked every time is code review (with good code reviewers).

Other than code review, I'd first try Page Heap. Page Heap takes a few seconds to set up and with luck it might pinpoint your problem.

If no luck with Page Heap, download Debugging Tools for Windows from Microsoft and learn to use the WinDbg. Sorry couldn't give you more specific help, but debuging multi-threaded heap corruption is more an art than science. Google for "WinDbg heap corruption" and you should find many articles on the subject.

Interrelated answered 18/6, 2009 at 4:20 Comment(0)
A
5

What type of allocation functions are you using? I recently hit a similar error using the Heap* style allocation functions.

It turned out that I was mistakenly creating the heap with the HEAP_NO_SERIALIZE option. This essentially makes the Heap functions run without thread safety. It's a performance improvement if used properly but shouldn't ever be used if you are using HeapAlloc in a multi-threaded program [1]. I only mention this because your post mentions you have a multi-threaded app. If you are using HEAP_NO_SERIALIZE anywhere, delete that and it will likely fix your problem.

[1] There are certain situations where this is legal, but it requires you to serialize calls to Heap* and is typically not the case for multi-threaded programs.

Amerce answered 18/6, 2009 at 0:25 Comment(4)
Yes: look at the application's compiler/build options, and ensure it's being built to linking against a "multi-threaded" version of the C run-time library.Lustreware
@Lustreware for the HeapAlloc style APIs this is different. It's actually a parameter that can be changed at heap creation time, not link time.Amerce
Oh. It didn't occur to me that the OP might be talking about that heap, and not about the heap in the CRT.Lustreware
@ChrisW, the question is rather vague but I just hit the problem I detailed ~1 week ago so it's fresh on my mind.Amerce
B
4

If these errors occur randomly, there is high probability that you encountered data-races. Please, check: do you modify shared memory pointers from different threads? Intel Thread Checker may help to detect such issues in multithreaded program.

Buchenwald answered 18/6, 2009 at 17:39 Comment(0)
S
3

You may also want to check to see whether you're linking against the dynamic or static C runtime library. If your DLL files are linking against the static C runtime library, then the DLL files have separate heaps.

Hence, if you were to create an object in one DLL and try to free it in another DLL, you would get the same message you're seeing above. This problem is referenced in another Stack Overflow question, Freeing memory allocated in a different DLL.

Sexennial answered 4/2, 2011 at 1:20 Comment(0)
I
2

In addition to looking for tools, consider looking for a likely culprit. Is there any component you're using, perhaps not written by you, which may not have been designed and tested to run in a multithreaded environment? Or simply one which you do not know has run in such an environment.

The last time it happened to me, it was a native package which had been successfully used from batch jobs for years. But it was the first time at this company that it had been used from a .NET web service (which is multithreaded). That was it - they had lied about the code being thread safe.

Intervocalic answered 18/6, 2009 at 0:29 Comment(0)
R
1

You can use VC CRT Heap-Check macros for _CrtSetDbgFlag: _CRTDBG_CHECK_ALWAYS_DF or _CRTDBG_CHECK_EVERY_16_DF.._CRTDBG_CHECK_EVERY_1024_DF.

Reorganize answered 7/11, 2011 at 11:28 Comment(0)
B
1

I'd like to add my experience. In the last few days, I solved an instance of this error in my application. In my particular case, the errors in the code were:

  • Removing elements from an STL collection while iterating over it (I believe there are debug flags in Visual Studio to catch these things; I caught it during code review)
  • This one is more complex, I'll divide it in steps:
    • From a native C++ thread, call back into managed code
    • In managed land, call Control.Invoke and dispose a managed object which wraps the native object to which the callback belongs.
    • Since the object is still alive inside the native thread (it will remain blocked in the callback call until Control.Invoke ends). I should clarify that I use boost::thread, so I use a member function as the thread function.
    • Solution: Use Control.BeginInvoke (my GUI is made with Winforms) instead so that the native thread can end before the object is destroyed (the callback's purpose is precisely notifying that the thread ended and the object can be destroyed).
Bootleg answered 23/5, 2012 at 16:40 Comment(0)
S
-1

I had a similar problem - and it popped up quite randomly. Perhaps something was corrupt in the build files, but I ended up fixing it by cleaning the project first then rebuilding.

So in addition to the other responses given:

What sort of things can cause these errors? Something corrupt in the build file.

How do I debug them? Cleaning the project and rebuilding. If it's fixed, this was likely the problem.

Strychnine answered 30/10, 2016 at 22:18 Comment(0)
L
-2

I have also faced this issue. In my case, I allocated for x size memory and appended the data for x+n size. So, when freeing it shown heap overflow. Just make sure your allocated memory sufficient and check for how many bytes added in the memory.

Leal answered 12/8, 2020 at 6:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.