开发者

C++ Program crash (C0000005 Access Violation) on call instruction

I have a crash that's puzzling me and that I thus far have found impossible to consistently reproduce. The code is compiled with Visual Studio 2008.

The (simplified, of course) source code looks like this:

class AbstractParentClass 
{
private:
    /* data members */
public:
    AbstractParentClass();
    /* 
       virtual functions ...
     */
}; 

class ChildClass : public AbstractParentClass
{
private:
    /* data members */
public:
    ChildClass();
    /* 
       overridden/implemented virtual functions ...
     */
};

void DifferentClass::func(const char ** strs)
{
     ChildClass child_class;
     int i = 0;
     [...]
}

The disassembly from the crash dump looks like this:

Library!DifferentClass::func:
612cab20 83ec58          sub     esp,58h
612cab23 56              push    esi
612cab24 57              push    edi
612cab25 8bf9            mov     edi,ecx
612cab27 8d4c2420        lea     ecx,[esp+20h]
612cab2b e8e053403f      call    a06cff10  
612cab30 8b742464        mov     esi,dword ptr [esp+64h]
[...]

Mapping the source of func() against the disassembly, it winds up looking like this:

Library!DifferentClass::func:
void DifferentClass::func(const char ** strs)
{
612cab20 83ec58          sub     esp,58h
612cab23 56              push    esi
612cab24 57              push    edi
612cab25 8bf9            mov     edi,ecx
     ChildClass child_class;
612cab27 8d4c2420        lea     ecx,[esp+20h]
612cab2b e8e053403f      call    a06cff10 
     int i = 0;
612cab30 8b742464        mov     esi,dword ptr [esp+64h]
[...]
}

In a successful run (different machine, though even on the same machine, the crash is not reliably reproducible), the only difference in the disassembly is the call instruction, which winds up mapping correctly to the address of the default constructor of ChildClass, like this:

00404e8b  call        ChildClass::ChildClass (40a3d0h)

rather than like:

612cab2b  call        a06cff10

So in the crash run, that a06cff10 address that serves as the call instruction's parameter seems to be coming from who-knows-where and is not mapped to anything in particular. And so, predictably, trying access that address (to get to the ChildClass default 开发者_如何学Cconstructor) is resulting in an access violation:

EXCEPTION_RECORD:  0012f688 -- (.exr 0x12f688)
ExceptionAddress: a06cff10
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: a06cff10
Attempt to read from address a06cff10

Any attempt to look at that address in the crash dump indeed indicates that the address is out of bounds for the process.

UPDATE: So after reading the response below from zvrba and looking further at it, the problematic call seems to be the first of a dozen or so function calls within a static library (that is in turn loaded by a DLL) that all have an incorrect function offset. They're not all functions in the same class. There are three or four different classes with functions affected, though all the classes (both invoking and being invoked) live in that same static library. In this first call which crashed things, the instruction was e8e053403f and the 3F4053E0 offset in that instruction should have been an offset of just 53E0. All of the other instances have the same offset problem. The offset in the instruction is 3F40XXXX, when it should be just XXXX. The extra 3F400000 is of course sending things off into Never Never Land. So far, I haven't picked up on an pattern with regard to which function addresses within the disassembly are valid and which or not. One member function of DifferentClass in the library will have all of its calls to ChildClass as bad, whereas another member function DifferentClass will have a different call into ChildClass look just fine.

Has anyone out there seen something like this/have any thoughts on likely causes thereof?


Did you maybe implement child class constructor in another DLL? What I suspect happens is that, in the crash run, the DLL is loaded at another address than its preferred address -- you can check that in the modules window in VS debugger. This in turn results in a call target being miscalculated (that particular call instruction is relative). The offset in the assembly (4 bytes after the E8 opcode) is also very weird and looks more like a relocation that has not been fixed up than a valid offset. How do you load that DLL?


It's hard to understand what's going on with most of the source code omitted, although from your comments and disassembly, it looks like the address of the ChildClass vtable is being corrupted. This would have several possible causes, e.g.

  • As @contactmatt mentioned, array/buffer overruns
  • using a destroyed object
  • using an unitialized variable
  • incorrectly casting/converting a variable/pointer
  • loading data from a buffer into an object without packing/checking the byte alignments
  • etc.

First, locate the vtable address and try stepping through the debugger, checking when the vtable memory gets overwritten. It's likely a few bytes above the location (40a3d0h) you pointed out:

  call        ChildClass::ChildClass (40a3d0h)

Then, look for the code that might be executing during that point in time.

Caveat: because the cause is pretty much unknown, finding the actual fix would mean reading through a lot of code/going through your source control versions to see any possible hazards. From experience, the problem (such as one of those mentioned) causing the corruption might not even be anywhere near the line of code with the access violation.


Not that this helps, but where I work I was fixing a Access Violation error result from accessing an out of bounds array in a C program. I was only able to reproduce it once in a while on my machine. The only fix we could do was to put a lot of 'array out of bounds' checks in place.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜