One day I was debugging a problem where a Waston dialog popped up on a process. What surprised me was that on the stack where Waston was triggered, there was a unmanaged C++ function with a try-catch(…) block. To my understanding, this block should catch any user mode exception thrown in Windows, including exception from RaiseException call (e.g, C++ exceptions), AV, stack overflow, and etc. Why an exception could escape such a block and become unhandled (thus Waston showed up)? I found the exception was a debug break. In X86, it is triggered by opcode 0xCC or “int 3”. When I debugged into VCRT’s EH code, I found catch (…) deliberately let debug break go. It does make sense: debug break is meant to stop the debugger so source code should never handle it. I just never realized it before.
Another interesting part is where this debug break was from, the code of the process never calls DebugBreak. After I debugged more, the problem turned out to be the bug I mentioned in my previous blog entry: a premature GC issue. managed code passed a Delegate to unmanaged code without telling GC to extend its lifetime. When unmanaged code called the callback, the managed Delegate object was already collected so unmanaged code called into garbage memory. The memory happened to be filled with 0xCC so when the process tried to execute this code, it fired int 3, then Waston kicked in.
This posting is provided “AS IS” with no warranties, and confers no rights.