Decoding the parameters of a thrown C++ exception (0xE06D7363), revisited


When I explained how to decode the parameters of a thrown C++ exception, I noted that the mysterious second parameter at index 1 "is a pointer to the object being thrown (sort of)."

I've since learned more about that mysterious second parameter at index 1: It really is a pointer to the object being thrown, no header or anything. I was faked out because in the crash dumps I looked at, the object being thrown was a pointer to a stack object, so we had two things on the stack (the pointer being thrown, and the object being pointed to), and therefore I could dump my way from one to the other.

Basically, I got lucky.

So that's the story of the second parameter. It's a pointer to the object being thrown, but if the object being thrown is itself a pointer, then you'll have to perform another indirection to find the underlying object.

Here's an example from a throw E_FAIL:

0:000> .exr 02e0f704
ExceptionAddress: 75dd4878 (KERNELBASE!RaiseException+0x00000048)
   ExceptionCode: e06d7363 (C++ EH exception)
  ExceptionFlags: 00000001
NumberParameters: 3
   Parameter[0]: 19930520
   Parameter[1]: 02e0fc6c
   Parameter[2]: 00d1263c
0:000> dd 02e0fc6c l1
02e0fc6c  80004005 // the thing being thrown is value

And here's throw "oops":

0:000> .exr 02a6f3e4
ExceptionAddress: 75dd4878 (KERNELBASE!RaiseException+0x00000048)
   ExceptionCode: e06d7363 (C++ EH exception)
  ExceptionFlags: 00000001
NumberParameters: 3
   Parameter[0]: 19930520
   Parameter[1]: 02a6f944
   Parameter[2]: 008d22b0
0:000> dd 02a6f944 l1
02a6f944  008d10e0
// since the thing being thrown is a pointer, we dump what it points to.
0:000> da 008d10e0
008d10e0  "oops" // what it points to

Bonus chatter: The layout of the mysterious third parameter (index 2) is explained in the ehdata.h header file that comes with Visual Studio.

Comments (9)
  1. MacIn173 says:

    How some can determine, whether it is a code or object address? (80004005 looks like a NTSTATUS with medium severity, but code could be of smaller value, couldn't it?)

    1. Joshua says:

      IsBadReadPtr

      Seriously, the code is correctly designed; you know the difference by looking at the format in the linked article.

      1. MacIn173 says:

        "IsBadReadPtr"
        What I'm asking here is basically if code stuffed in structure instead of object address can be one of valid memory addresses. If so, then IsBadReadPtr is no use.

        "you know the difference by looking at the format in the linked article"
        Not a chance, the linked article is all about exception object, while I'm asking about code.

        1. Darran Rowe says:

          The type information is part of that third parameter, that entire parameter is all about describing what type was thrown.

          1. MacIn173 says:

            Ok, this was the missing part. Thank you.

    2. Douglas says:

      Well, I checked a list of "common HRESULT values": https://msdn.microsoft.com/en-us/library/windows/desktop/aa378137(v=vs.85).aspx
      I note that all of them are uint32 (or similar). I also note there're two (kinds of) values that matter. There's S_OK, which is zero. So someone threw a null pointer, or threw S_OK (... which makes no sense when you think about it). All error values are negative.

      In (old) 32 bit land, were those pointers, they would be in the higher half, so usermode code has no business with them (I think).

      With LargeAddressAware (or 64 bit), that doesn't help you. Although in x64 (currently) addresses are 48 bits and sign extended, so anything 0x0001...0000 or higher is garbage. I don't know if the high half of the pointer would be left as garbage or not, though.

      That only helps sometimes, not always.

      I'd also look at the alignment (if you act as though it were a pointer). "Object allocated on an odd address? Hmm... I don't think so."

      Naturally, because you're debugging your (or your coworker's) own code (right?), you have a code address and a map file (you did keep the map file, right?), so you can load that in the debugger. It then points you to frobnicator/dofrob.cpp:183 (which you have, right?) and when you check it you find "throw foo->favorite_error;" or something like that. Then you (should) know the type, so you go back to the debugger to look at the values, etc. etc.

      1. MacIn173 says:

        "All error values are negative"
        E_FAIL is NTSTATUS kind of value with 'warning' severity. My concern was that some can craft one of 'informational' severity, that means code would begin with 0x4... which could be a readable address as well.

        "“Object allocated on an odd address? Hmm… I don’t think so.”"
        Let's say, the code is 0x401000E0. And this is both valid address/inf code.

        "Naturally, because you’re debugging your (or your coworker’s) own code (right?)"
        I should have formulated it better, I guess. "How some could determine" = "what algorithm machine should use to determine", eg I was thinking from CPU's point of view.
        But one way or another, answer above ^^^ declares, that 3rd par has details.

  2. Darran Rowe says:

    Well, tbh, I can't find the ehdata.h file in the VC2015 sources.

  3. Scarlet Manuka says:

    Oddly enough I was struggling with a rash of 0xE06D7363 errors only a day or two ago. I knew what the cause was though, the tools I was using didn't like dealing with 150MB of text being dumped on them. (Lots and lots of SQL statements.) Also Word's Replace All behaviour is surprisingly wonky when faced with a file of that size, even though it didn't give me an error.

    Fortunately I found a better way to deal with the problem and I don't have to repeat the exercise when we get updated versions of the data.

Comments are closed.

Skip to main content