Debugging Heap Corruptions in Production (Release Mode) MSVC++ Windows Applications with Global Flags

Say you have a Release mode MSVC++ Win32 Console Application with the following code:

#include "stdafx.h"
#include <iostream>

using namespace std;

void CorruptTheHeap()
{
	// Create a pointer to an integer.
	int *reallyBadlyCodedIntegerAllocation;
	// Allocate insufficient space for this memory space to store an actual integer.
	cout << "The expected memory size of this type is " << sizeof(int) << " bytes, which is " << 8*sizeof(int) << " bits." << endl;
	int sizeToAllocateForThisType = 0;
	cout << "The actual size of this integer will be set to " << sizeToAllocateForThisType << " bytes." << endl;
	reallyBadlyCodedIntegerAllocation = (int *)malloc(sizeToAllocateForThisType);
	// Try to set the integer to an integer value (causing a memory leak at this point since we are writing over the bounds of the memory we actually own).
	*reallyBadlyCodedIntegerAllocation = 0;
	// Trying to free this integer would cause a heap corruption because it will try to free the size of the type, but we have allocated it to be nothing. In a Release build, this will cause a crash.
	free(reallyBadlyCodedIntegerAllocation);
}

int _tmain(int argc, _TCHAR* argv[])
{
	CorruptTheHeap();
	cout << "Press any key to exit." << endl;
	cin.get();
	return 0;
}

Make sure the application’s project settings build it so that its *.pdb file(s) is or are in the same folder as where you execute it. Now get WinDbg, which contains Global Flags. For this example, my application is built in Release (must be in Release) as an x64 architecture application, so the next step for me is to start Global Flags (X64). A simple Windows start screen search for Global Flags should uncover it, but for me it installed at C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x64\gflags.exe. Open it up, go to the Image File tab. Type in the name of your Image file. For me, my Console Application compiles and runs as HeapCorruptor.exe, so I typed that in, and pressed the TAB key on my keyboard. You’ll notice that when running Global Flags, it seems to keep track of executables based on their name, so when you restart and go through the same process of typing in the executable’s name and press the TAB key, it loads your last configuration (I know, its kind of weird and takes a bit of getting used to).

Check off Debugger and set its path to the path of your WinDbg executable. For me, a default setup of the debugging tools installed WinDbg at “C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x64\windbg.exe”. Then set up any other necessary flags, as I have in the screenshot below, hit Apply, and then OK to close it down.

What you need to to set in Global Flags to catch heap corruptions in production.
Settings for detecting heap corruptions using Global Flags.

Now, run your executable. Global Flags will start up along with it. It starts paused, so type in g (for go) to get it to continue, or hit F5 in Global Flags. After you type in g and hit enter, you should see an error as the application starts up and runs:

0:000> g


===========================================================
VERIFIER STOP 000000000000000F: pid 0x3DC0: corrupted suffix pattern 

	0000003E3B071000 : Heap handle
	0000003E3C191500 : Heap block
	0000000000000001 : Block size
	0000003E3C191501 : corruption address
===========================================================
This verifier stop is not continuable. Process will be terminated 
when you use the `go' debugger command.

We have caught a heap corruption at memory address 0000003E3C191500. Type in !heap –p –a 0000003E3C191500 to get more information on the error:

    address 0000003e3c191500 found in
    _HEAP @ 3e3c150000
              HEAP_ENTRY Size Prev Flags            UserPtr UserSize - state
        0000003e3c1914b0 0008 0000  [00]   0000003e3c191500    00001 - (busy)
        7ffc110a83f3 verifier!VerifierDisableFaultInjectionExclusionRange+0x00000000000022ff
        7ffc4e438e98 ntdll!RtlpNtMakeTemporaryKey+0x0000000000000330
        7ffc4e3f333a ntdll!memset+0x00000000000148fa
        7ffc4e3774e7 ntdll!RtlAllocateHeap+0x0000000000000187
        7ffc110c17ab verifier!VerifierGetPropertyValueByName+0x00000000000133cf
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SYSTEM32\MSVCR120.dll - 
        7ffc10e96a57 MSVCR120!malloc+0x000000000000005b
*** WARNING: Unable to verify checksum for HeapCorruptor.exe
        7ff7698d1316 HeapCorruptor!CorruptTheHeap+0x00000000000000a6
        7ff7698d1339 HeapCorruptor!wmain+0x0000000000000009
        7ff7698d1e37 HeapCorruptor!__tmainCRTStartup+0x000000000000010f
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\KERNEL32.DLL - 
        7ffc4bc216ad KERNEL32!BaseThreadInitThunk+0x000000000000000d
        7ffc4e3b4629 ntdll!RtlUserThreadStart+0x000000000000001d

This is great because it shows us some important bits of information, like the call-stack that caused our corruption. In particular, we can see that CorruptTheHeap() was called and that, while running it, Global Flags was then unable to verify a check-sum, so we can pinpoint the problem to be in that function…sometimes Global Flags may show you even more granular methods of underlying framework code that you are using to help you drill down to the actual function causing problems, but in a nutshell that’s essentially it. Make sure to follow the steps above and disable all check-boxes for the context of that executable’s name in Global Flags, or else it will always start with your executable.

Alexandru

"To avoid criticism, say nothing, do nothing, be nothing." - Aristotle

"It is wise to direct your anger towards problems - not people; to focus your energies on answers - not excuses." - William Arthur Ward

"Science does not know its debt to imagination." - Ralph Waldo Emerson

"Money was never a big motivation for me, except as a way to keep score. The real excitement is playing the game." - Donald Trump

"All our dreams can come true, if we have the courage to pursue them." - Walt Disney

"Mitch flashes back to a basketball game held in the Brandeis University gymnasium in 1979. The team is doing well and chants, 'We're number one!' Morrie stands and shouts, 'What's wrong with being number two?' The students fall silent." - Tuesdays with Morrie

I'm not entirely sure what makes me successful in general programming or development, but to any newcomers to this blood-sport, my best guess would be that success in programming comes from some strange combination of interest, persistence, patience, instincts (for example, someone might tell you that something can't be done, or that it can't be done a certain way, but you just know that can't be true, or you look at a piece of code and know something doesn't seem right with it at first glance, but you can't quite put your finger on it until you think it through some more), fearlessness of tinkering, and an ability to take advice because you should be humble. Its okay to be wrong or to have a bad approach, realize it, and try to find a better one, and even better to be wrong and find a better approach to solve something than to have had a bad approach to begin with. I hope that whatever fragments of information I sprinkle across here help those who hit the same roadblocks.

Leave a Reply

Your email address will not be published. Required fields are marked *