Exception Handler Bug

Feedback? Questions? Comments? All discussions on the articles and tutorials hosted or developed by us go in here.

Moderator: Moderators

Exception Handler Bug

Postby Andyhhp » Mon Oct 20, 2008 6:54 pm

Hi,

When trying to test my exception handling routines, I had a stack problem and realized that it is a bug current in all the exception handling examples in the tutorials.

Here is an explanation and a solution to it.


When an interrupt fires, the stack looks like this:

...
EFlags
CS
EIP <-- esp points here
[Error] <---+ or here depending on the exception
...

However, given a function in C++ such as:
Code: Select all
void Div0(unsigned int eip,unsigned int cs,unsigned int eflags)
{
...
}

This is compiled expecting the stack to look like:

...
eflags
cs
eip
retn addr
prev ebp <-- ebp points here
local vars ...

The parameters are referenced using
Code: Select all
dword ptr [ebp +- num]


Therefore, the code that the C++ compiler and linker generates has a 4 byte offset because there is no return address for an interrupt handler. This causes all the parameters to index the wrong elements on the stack.

A workaround for this exists by subtracting 4 bytes from ebp so all the parameter references become correct. This has the side effect that any local variables will be 4 bytes lower in memory than expected but this will only have any effect if you try using inline assembly to reference them. One final point to say is that, because this isn't a naked function, the stack must be restored before the iret instruction.

Therefore, here is a function that works properly:
Code: Select all
void Div0(unsigned int eip,unsigned int cs,unsigned int eflags)
{
__asm sub ebp,4

//deal with Div0

__asm{
pop ebp
iretd
}
}


~Andrew
Image
Andyhhp
Moderator
 
Posts: 387
Joined: Tue Oct 23, 2007 10:05 am
Location: 127.0.0.1

Postby Mike » Tue Oct 21, 2008 1:00 am

That (technically) was not a bug. The original reason it was omitted is because in the earlier demos all exception handlers simply halted the system thus we didnt need to worry about the stack.

Hm... Perhaps I shouldnt do that though. Ill see if I can update them. Thanks! :D
User avatar
Mike
Site Admin
 
Posts: 462
Joined: Sat Oct 20, 2007 7:58 pm

Postby Andyhhp » Tue Oct 21, 2008 9:51 am

Fair enough.

I came across this when trying to print out the exact address of the exception for debugging purposes.

Since I made this post, I have found a small problem that doesn't have a straight forward solution.

Because the register states need to be maintained exactly, and this is not a naked function, the compiler pushes all the used registers (other than eax) after the frame pointer and before the first line of code. It clears up again after itself before it returns (again except for eax).

The problem is that it doesn't see iretd as a return from the function so doesn't clear up before that.

The way I see it, there are 3 solutions, 2 are neither very elegent and 1 seems to go against the idea of using C++ over assembly.

1) Use naked functions and explicitly pusha/popa at the beginning and end. The problem with this is you can't have local variables.

2) Use the generated asm code from the compiler to see which registers need to be pop'd before iretd and hope that adding that code doesn't cause the optimizer to use different registers. The problem with this is that it messes with local variables because the the stack is changing in the area where local variables are stored

3) Write a small function entirely in assembly that handles the stack properly, which calls the normal C/C++ exception handler.

I am currently working on implementing #3 and will post an example when I have it fully working (i currently have it using C++ naming, as well as namespaces but I am working on getting it to call other C++ named functions).

~Andrew
Image
Andyhhp
Moderator
 
Posts: 387
Joined: Tue Oct 23, 2007 10:05 am
Location: 127.0.0.1

Postby Mike » Tue Oct 21, 2008 11:47 am

Because the register states need to be maintained exactly, and this is not a naked function, the compiler pushes all the used registers (other than eax) after the frame pointer and before the first line of code. It clears up again after itself before it returns (again except for eax).

The problem is that it doesn't see iretd as a return from the function so doesn't clear up before that.

You need to use an _asm iretd instruction before the routines end as the compiler will not do that for you.

If your routines are using the _cdecl calling convention, the parameter list for the routine contains the values pushed by the processor on the current stack. You will need to clean up the values pushed by the compiler manually. This is fairly easy. I can see if I can post an example later if you want (cant do it now--got to go to work :) )
User avatar
Mike
Site Admin
 
Posts: 462
Joined: Sat Oct 20, 2007 7:58 pm

Postby Andyhhp » Tue Oct 21, 2008 12:17 pm

lol - thats what I intended to say - i wasn't very clear.

The problem with manually clearing up the stack below EIP/Error is that because of optimization, adding the relevant pop instructions to maintain all the registers sometimes causes the compiler to change which registers it uses, which still leaves you with stack corruption.

I have found a way to create asm stub functions that will properly maintain the stack, then call proper C++ functions as handlers.

However, I have a hardware lab to go to now so I shall post details when I get back

~Andrew
Image
Andyhhp
Moderator
 
Posts: 387
Joined: Tue Oct 23, 2007 10:05 am
Location: 127.0.0.1

Postby Andyhhp » Wed Oct 22, 2008 12:43 am

Here is my solution. I will admit it is fairly heavily compiler dependent but the initialization functions and some of the setup is as well so meh - it will have to do ^_^

Firstly, I will explain the structure I have taken in my code which may help to explain the way I went about this.

I am structuring my code in a way not unlike the java libraries. As such, all the code for interrupt and exception handling is in namespace called ISR.

Specifically, I have a function called Ex_Div0 that is nominally the int0 interrupt handler.

However, due to the stack problems that I illustrated in my first post, and the repeated problems I discovered after, I have had to implement this strategy to solve the problem. The good news that I believe I have fully tested this now so I don't foresee any more problems (is only but its nice to hope)

Here is the relevant snippets of my code:

isr.cpp
Code: Select all
namespace ISR {

struct IntStk
{
   unsigned int eip;
   unsigned int cs;
   unsigned int eflags;
};

struct EIntStk
{
   unsigned int error;
   unsigned int eip;
   unsigned int cs;
   unsigned int eflags;
};

void Ex_Div0Stub();
void Ex_Div0(void *ptr);

void Initialize(){
...
   Install_ISR(0,(ISR_FUNCTION)Ex_Div0Stub,0x08,flags);
...
}

void Ex_Div0(void *ptr){
      IntStk * data = (IntStk*)ptr;

      Print("\n\nDiv0 Exception occurred - Addr: 0x");
      Hex2Str(data->cs);
      Print(":0x");
      Hex2Str(data->eip);
      Print(", EF: 0x");
      Hex2Str(data->eflags);
      Print('\n');

      unsigned char ins = *((unsigned char*)data->eip);
      if(ins == 0xF7 || ins == 0xF6)
      {
         PrintLn("Skipping exceptional opcode");
         data->eip += 2;
      }
      else
         PrintLn("No exceptional opcode found - returning");
}

};

2 functions, Ex_Div0 and Ex_Div0Stub are declared but only Ex_Div0 is defined. It is a simple function that prints out the offending address, then checks to see wether the offending opcode is a div/idiv instruction (as opposed to an int0) and if so, adds 2 to the instruction pointer to skip it (not ideal but best until I have C++ language exception handling sorted). Then this function returns normally. Notice that the Ex_Div0Stub function is the one set as the interrupt handler, not Ex_Div0 itself.

isrstub.asm
Code: Select all
TITLE isrstub.asm
.686P
.model flat

?EX_Div0@ISR@@YAXPAX@Z PROTO SYSCALL

_TEXT SEGMENT
?Ex_Div0Stub@ISR@@YAXXZ PROC

   push eax
   
   lea eax,[esp+4]
   push eax
   call ?EX_Div0@ISR@@YAXPAX@Z
   add esp,4
   
   pop eax
    iretd
   
?Ex_Div0Stub@ISR@@YAXXZ ENDP
_TEXT ENDS

END

This is a .asm file which, when included into an MSCV project, automatically get assembled (instead of compiled) then linked into the final program. The top 3 lines are setup parameters for the file. I used the same settings that the compiler generates and spits out in the code listings, in an attempt to preemptively prevent any bugs due to code of mis-matched versions.

The line "?EX_Div0@ISR@@YAXPAX@Z PROTO SYSCALL" declares the name mangled "void Ex_Div0(void *ptr)" as a function. Similarly, "?Ex_Div0Stub@ISR@@YAXXZ PROC" defines the name mangled "void Ex_Div0Stub()". By using these names, the linker links them together and all is fine as far function pointers are concerned.

The ?Ex_Div0Stub@ISR@@YAXXZ function itself is the interrupt handler. It stored the value eax, the only register which the compiler wont maintain in the Ex_Div0 function. It then pushes a pointer to the start of the stack information from the interrupt as the void* parameter for Ex_Div0. Then it calls the function itself. After the function returns, it restores eax and calls iretd.

By passing the address of the interrupt stack information as a parameter into the Ex_Div0 function, it allows that function to alter things like the return address if needs be. By the parameter being a void pointer, it allows it to determine whether or not an error code was pushed on the stack (important for some handlers) rather than assuming one will be present or not depending on the way its coded.


I hope this is clear but feel free to ask questions about any confusion/misunderstandings.

~Andrew
Image
Andyhhp
Moderator
 
Posts: 387
Joined: Tue Oct 23, 2007 10:05 am
Location: 127.0.0.1

Postby Mike » Sat Nov 01, 2008 11:53 pm

Im going to see if we can get the next demo's exception handlers properly working. If it works, Ill update all of the previous demos.

I dont plan on releasing the next tutorial's demo until its working, so lets hope for the best... :)
User avatar
Mike
Site Admin
 
Posts: 462
Joined: Sat Oct 20, 2007 7:58 pm

Postby Andyhhp » Sun Nov 02, 2008 12:01 am

Yeah - I'm sure the tutorial on paged memory management would go better with a functioning page fault handler

Sadly, due to work, I have yet to continue this method beyond the div0 handler but I should be continuing soon.

If there are any problems with this method, please say so I can modify my own ^_^

Thanks,

Andrew
Image
Andyhhp
Moderator
 
Posts: 387
Joined: Tue Oct 23, 2007 10:05 am
Location: 127.0.0.1

Postby michael » Sat Nov 08, 2008 12:01 am

Wow. Thanks. :) I've been trying for ages to make a keyboard interrupt handler that worked but it always crashed on iret, and this fixes that problem too.. although its really the same sortof thing. (If you cant read this properly my browsers being very anoying right now :x )
michael
 
Posts: 29
Joined: Thu Nov 15, 2007 12:06 am

Postby Andyhhp » Sat Nov 08, 2008 11:14 pm

Lol np - I posted it here to help people. Glad to see it's appreciated.

~Andrew
Image
Andyhhp
Moderator
 
Posts: 387
Joined: Tue Oct 23, 2007 10:05 am
Location: 127.0.0.1

Re: Exception Handler Bug

Postby Mezo » Sat Aug 13, 2011 10:17 pm

Thank you very much Andyhhp :D
you helped me very much :D
Mezo
 
Posts: 7
Joined: Wed Oct 20, 2010 1:20 pm


Return to Article Feedback

Who is online

Users browsing this forum: No registered users and 2 guests

cron