2

This is a follow-up to my previous question regarding exceptions.

I have some legacy code that I am attempting to maintain. It has a custom memory management component that I am having difficulty understanding.

My understanding of the system is as follows:

Calling function asks for a some memory to be allocated for it, providing an initial amount of memory needed (needed), and a maximum amount (max). This calls:

base = VirtualAlloc(0, max, MEM_RESERVE, PAGE_NOACCESS);

Which I understand reserves the memory but does not provide access. In other words, if I try to write to the reserved segment, I would get an access violation.

It then calls:

VirtualAlloc(base, needed, MEM_COMMIT, PAGE_READWRITE);

Which makes needed amount of memory starting at base accessible.

The sticky part comes when trying to detect when more memory needs to be made accessible. My understanding is that the system attempts to catch access violation exceptions when they happen and call VirtualAlloc on the address to make the memory accessible.

It does this by declaring the following method:

unsigned long __cdecl
exceptionCatch(struct _EXCEPTION_RECORD* er, void*, struct _CONTEXT* cr, void*)
{
    if( er->ExceptionCode == EXCEPTION_ACCESS_VIOLATION
      && ExtendBuffer( (void*)er->ExceptionInformation[1] ) )
        return ExceptionContinueExecution;

    return ExceptionContinueSearch;
}

Then, it registers this as the exception handler for the top of the stack (I think), using this particularly horrible piece of code:

void __cdecl SetHandler(bExceptionRegistration& v)
{
    __asm
    {
        mov     eax, 8[ebp]     ; get exception register record to install
        mov     ecx, fs:[0]     ; get current head of chain

        cmp     ecx, eax        ; should we be at head?
        jb      search
        mov     [eax], ecx      ; save current head
        mov     fs:[0], eax     ; install new record at head
        jmp     short ret1
search:
        cmp     [ecx], eax      ; at proper location yet?
        ja      link
        mov     ecx, [ecx]      ; get next link
        jmp     search
link:
        mov     edx, [ecx]
        mov     [eax], edx      ; point to next
        mov     [ecx], eax
ret1:
    }
}

This method is called by instantiating a particular class in a method scope. It looks like it only applies the handler to the current stack context; as in, exceptions thrown in called functions are not handled by the current method if the exception is not propagated to the current method.

The result of all this is that not only is the access violation not caught, but it disables exception handling at the current top of the stack. I have set break points in the exceptionCatch function and execution doesn't appear to enter it.

I suppose my main questions are:

  1. Is there any particular reason why this shouldn't work? Edit: based on my own testing and comments here, I think the assembly code is the problem area.
  2. More importantly, is there a better way to do what I think the code is attempting to do?

I don't think something like set_unexpected is feasible, since the memory management is applying only to this particular library and the client application may (and in our case, does) have its own unexpected exception handler.

Edit:

The setting and unsetting of the handler per stack is done by declaring a class bExceptionRegistration with the following class constructor and destructor:

bExceptionRegistration :: bExceptionRegistration() : function(exceptionCatch)
{
    SetHandler(*this);
}

bExceptionRegistration :: ~bExceptionRegistration()
{
    UnsetHandler(*this);
}

So, to actually set the handler for a particular stack scope, you would have:

void someFunction()
{
    bExceptionRegistration er;
    // do some stuff here
}

Edit: I'm guessing that probably the most appropriate solution to all this is to replace the bExceptionRegistration declarations in the code with __try, __except blocks. I was hoping to avoid this however, as it is in a lot of places.

Community
  • 1
  • 1
tvStatic
  • 921
  • 1
  • 9
  • 26
  • 1
    the installation of the handler looks suspect. anyway, there's an example at http://msdn.microsoft.com/en-us/library/windows/desktop/aa366803%28v=vs.85%29.aspx – Cheers and hth. - Alf Dec 02 '11 at 05:38
  • I suppose the original intention was to do the same thing as `__try, __except` without actually dotting the code with lots of those clauses. This is helpful, thanks! – tvStatic Dec 02 '11 at 05:44
  • 1
    The idea of this code appears to be similar to that of mine [here](http://stackoverflow.com/q/8004945/968261), except your code is less clear. The problem with this approach is that one needs to implement try, __try and exception filters extremely carefully so as not to swallow or disable someone else's exceptions or corrupt the program state. – Alexey Frunze Dec 02 '11 at 12:05

1 Answers1

2

I'm not 100% sure about this without seeing more code. It doesn't register the exception handler at the top of the stack but it uses a trick to insert the exception handling where the EXCEPTION_REGISTRATION structure is defined. So for example (maybe in your case it's implemented a bit differently):

void function3(EXCEPTION_REGISTRATION& handler)
{
    SetHandler(handler);
    //Do other stuff
}
void function2(EXCEPTION_REGISTRATION& handler)
{
    __try
    {
        //Do something
        function3(handler);
    }
    __except(expression)
    {
        //...
    }
}

void function()
{
    EXCEPTION_REGISTRATION handler;
    //..Init handler
    function2(handler)
}

When you call SetHandler it will insert the exception handling like it was in the scope of function. So in this case at the moment you call SetHandler it will appear as if there is a __try __except block in function.

Therefor if there's an exception inside function3 the handler in function will first be called and if that handler doesn't handle it, the handler installed by SetHandler will be called.