Unexpected user breakpoint in NTDLL.DLL

Updated: 01.09.2005

We were in the middle of the debugging session, when suddenly the debugger displayed a message similar to the following:

User breakpoint called from code at 0x77fa018c

or the following:

Unhandled exception at 0x77f767cd (ntdll.dll) in myapp.exe: User breakpoint.

What happened? We did not even set any breakpoints, so why there is a user breakpoint? The problem starts looking even stranger when we notice that the application works fine when we start it outside of the debugger! But under debugger it keeps showing the message about user breakpoint, so that we cannot effectively debug our application anymore.

Searching for an answer, we look at Debug Output window. And yes, there is another message! For example:

HEAP[myapp.exe]: Heap block at 00360F78 modified at 00360F90 past requested size of 10

Aha, it looks like the heap is corrupted. The contents of Call Stack window also point to the heap:

NTDLL! DbgBreakPoint@0 address 0x77fa018c
NTDLL! RtlpBreakPointHeap@4 + 38 bytes
NTDLL! RtlpCheckBusyBlockTail@4 + 106 bytes
NTDLL! RtlpValidateHeapEntry@12 + 146975 bytes
NTDLL! RtlDebugFreeHeap@12 + 191 bytes
NTDLL! RtlFreeHeapSlowly@12 + 70765 bytes
NTDLL! RtlFreeHeap@12 + 4078 bytes
MYAPP! free + 102 bytes
MYAPP! operator delete(void *) + 9 bytes
main(int 1, char * * 0x003610b0) line 21 + 15 bytes
MYAPP! mainCRTStartup + 180 bytes
KERNEL32! BaseProcessStart@4 + 130958 bytes

It turns out that when we start the application under debugger, the operating system uses a special kind of heap - Debug Win32 heap - instead of the normal heap. Whenever an operation on the heap is performed (such as allocating or freeing memory), the debug heap manager checks integrity of the heap entries and internal structures. If it founds a corruption, it notifies us via a hard-coded breakpoint, which is reported by the debugger.

How can we find the reason of the heap corruption? We can of course employ a specialized runtime checker application (such as BoundsChecker, Purify or Memory Validator), but I prefer to use PageHeap, because it is part of the operating system (since Windows 2000 SP2) and is easy to use. Detailed technical information about PageHeap can be found in KB286470.

We can enable Full PageHeap with the help of the following Registry entries:

HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\yourapp.exe
    GlobalFlag = REG_DWORD 0x2000000
    PageHeapFlags = REG_DWORD 0x3

(replace yourapp.exe with the real name of your executable)

If you have Debugging Tools for Windows installed, there is another way to achieve the same - run the following command:

gflags -p /enable yourapp.exe /full

(gflags.exe can be found in the installation directory of Debugging Tools)

After we have enabled Full PageHeap, we should run the application under debugger and wait for access violations or hard-coded breakpoints. After an access violation occurred or a breakpoint has been hit, examination of the current source line and the call stack usually allows us to immediately see who is corrupting the heap.

Note also that it is recommended to link the application with release version of CRT library (that is, use /ML, /MT or /MD compiler option (or the corresponding IDE equivalent) instead of /MLd, /MTd or /MDd option). This is because debug version of CRT library uses its own heap checking infrastructure (by the way, not as powerful as Full PageHeap), which can overlap with PageHeap and hide some errors instead of exposing them.

Another situation that worth talking about is when we are debugging a DLL that is loaded by a large application (often a 3rd party application). Since there is probably no point in checking the whole application with PageHeap, we can ask it to check only the heap entries that are allocated by our DLL:

gflags -p /enable yourapp.exe /full /dlls yourdll.dll