Author: sir_richard Date: Tue Jan 19 09:20:12 2010 New Revision: 45142
URL: http://svn.reactos.org/svn/reactos?rev=45142&view=rev Log: [NTOS]: Implement "Edited Trap Frame" exit. This funky trick is actually how NT emulates longjmp/setjmp when doing an NtContinue: it allows arbitrary return with a new CS/ESP. [NTOS]: Implement C version of KiServiceExit, the second system call exit routine. This one sets a new EAX value to be returned to the caller and is used by system calls. [NTOS]: Implement NtContinue in C instead of ASM. Due to the changes above, this can now be done in C and use the new KiServiceExit.
Modified: trunk/reactos/ntoskrnl/include/internal/ke.h trunk/reactos/ntoskrnl/include/internal/trap_x.h trunk/reactos/ntoskrnl/ke/i386/trap.s trunk/reactos/ntoskrnl/ke/i386/traphdlr.c
Modified: trunk/reactos/ntoskrnl/include/internal/ke.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/include/internal/k... ============================================================================== --- trunk/reactos/ntoskrnl/include/internal/ke.h [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/include/internal/ke.h [iso-8859-1] Tue Jan 19 09:20:12 2010 @@ -932,6 +932,14 @@ VOID );
+NTSTATUS +NTAPI +KiContinue( + IN PCONTEXT Context, + IN PKEXCEPTION_FRAME ExceptionFrame, + IN PKTRAP_FRAME TrapFrame +); + VOID FASTCALL KiServiceExit2(
Modified: trunk/reactos/ntoskrnl/include/internal/trap_x.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/include/internal/t... ============================================================================== --- trunk/reactos/ntoskrnl/include/internal/trap_x.h [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/include/internal/trap_x.h [iso-8859-1] Tue Jan 19 09:20:12 2010 @@ -322,3 +322,35 @@ : "%esp" ); } + +FORCEINLINE +VOID +KiEditedTrapReturn(IN PKTRAP_FRAME TrapFrame) +{ + /* Regular interrupt exit */ + __asm__ __volatile__ + ( + "movl %0, %%esp\n" + "movl %c[a](%%esp), %%eax\n" + "movl %c[b](%%esp), %%ebx\n" + "movl %c[c](%%esp), %%ecx\n" + "movl %c[d](%%esp), %%edx\n" + "movl %c[s](%%esp), %%esi\n" + "movl %c[i](%%esp), %%edi\n" + "movl %c[p](%%esp), %%ebp\n" + "addl $%c[e],%%esp\n" + "movl (%%esp), %%esp\n" + "iret\n" + : + : "r"(TrapFrame), + [a] "i"(KTRAP_FRAME_EAX), + [b] "i"(KTRAP_FRAME_EBX), + [c] "i"(KTRAP_FRAME_ECX), + [d] "i"(KTRAP_FRAME_EDX), + [s] "i"(KTRAP_FRAME_ESI), + [i] "i"(KTRAP_FRAME_EDI), + [p] "i"(KTRAP_FRAME_EBP), + [e] "i"(KTRAP_FRAME_ERROR_CODE) /* We *WANT* the error code since ESP is there! */ + : "%esp" + ); +}
Modified: trunk/reactos/ntoskrnl/ke/i386/trap.s URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ke/i386/trap.s?rev... ============================================================================== --- trunk/reactos/ntoskrnl/ke/i386/trap.s [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/ke/i386/trap.s [iso-8859-1] Tue Jan 19 09:20:12 2010 @@ -523,52 +523,10 @@
.func NtContinue@8 _NtContinue@8: - - /* NOTE: We -must- be called by Zw* to have the right frame! */ - /* Push the stack frame */ - push ebp - - /* Get the current thread and restore its trap frame */ - mov ebx, PCR[KPCR_CURRENT_THREAD] - mov edx, [ebp+KTRAP_FRAME_EDX] - mov [ebx+KTHREAD_TRAP_FRAME], edx - - /* Set up stack frame */ - mov ebp, esp - - /* Save the parameters */ - mov eax, [ebp+0] - mov ecx, [ebp+8] - - /* Call KiContinue */ - push eax - push 0 - push ecx - call _KiContinue@12 - - /* Check if we failed (bad context record) */ - or eax, eax - jnz Error - - /* Check if test alert was requested */ - cmp dword ptr [ebp+12], 0 - je DontTest - - /* Test alert for the thread */ - mov al, [ebx+KTHREAD_PREVIOUS_MODE] - push eax - call _KeTestAlertThread@4 - -DontTest: - /* Return to previous context */ - pop ebp - mov esp, ebp - jmp _KiServiceExit2 - -Error: - pop ebp - mov esp, ebp - jmp _KiServiceExit + /* Call C code */ + mov ecx, [esp+4] + mov edx, [esp+8] + jmp @NtContinueHandler@8 .endfunc
/* HARDWARE TRAP HANDLERS ****************************************************/
Modified: trunk/reactos/ntoskrnl/ke/i386/traphdlr.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ke/i386/traphdlr.c... ============================================================================== --- trunk/reactos/ntoskrnl/ke/i386/traphdlr.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/ke/i386/traphdlr.c [iso-8859-1] Tue Jan 19 09:20:12 2010 @@ -54,6 +54,9 @@ IN UCHAR Skip) { KTRAP_EXIT_SKIP_BITS SkipBits = { .Bits = Skip }; + PULONG ReturnStack; + + /* Debugging checks */ KiExitTrapDebugChecks(TrapFrame, SkipBits);
/* Restore the SEH handler chain */ @@ -81,12 +84,42 @@
/* Check if the trap frame was edited */ if (__builtin_expect(!(TrapFrame->SegCs & FRAME_EDITED), 0)) - { - /* Not handled yet */ - UNIMPLEMENTED; - KiDumpTrapFrame(TrapFrame); - DbgBreakPoint(); - while (TRUE); + { + /* + * An edited trap frame happens when we need to modify CS and/or ESP but + * don't actually have a ring transition. This happens when a kernelmode + * caller wants to perform an NtContinue to another kernel address, such + * as in the case of SEH (basically, a longjmp), or to a user address. + * + * Therefore, the CPU never saved CS/ESP on the stack because we did not + * get a trap frame due to a ring transition (there was no interrupt). + * Even if we didn't want to restore CS to a new value, a problem occurs + * due to the fact a normal RET would not work if we restored ESP since + * RET would then try to read the result off the stack. + * + * The NT kernel solves this by adding 12 bytes of stack to the exiting + * trap frame, in which EFLAGS, CS, and EIP are stored, and then saving + * the ESP that's being requested into the ErrorCode field. It will then + * exit with an IRET. This fixes both issues, because it gives the stack + * some space where to hold the return address and then end up with the + * wanted stack, and it uses IRET which allows a new CS to be inputted. + * + */ + + /* Set CS that is requested */ + TrapFrame->SegCs = TrapFrame->TempSegCs; + + /* First make space on requested stack */ + ReturnStack = (PULONG)(TrapFrame->TempEsp - 12); + TrapFrame->ErrCode = (ULONG_PTR)ReturnStack; + + /* Now copy IRET frame */ + ReturnStack[0] = TrapFrame->Eip; + ReturnStack[1] = TrapFrame->SegCs; + ReturnStack[2] = TrapFrame->EFlags; + + /* Do special edited return */ + KiEditedTrapReturn(TrapFrame); }
/* Check if this is a user trap */ @@ -212,6 +245,24 @@
/* Now exit the trap for real */ KiExitTrap(TrapFrame, KTE_SKIP_PM_BIT); +} + +VOID +FASTCALL +KiServiceExit(IN PKTRAP_FRAME TrapFrame, + IN NTSTATUS Status) +{ + /* Disable interrupts until we return */ + _disable(); + + /* Check for APC delivery */ + KiCheckForApcDelivery(TrapFrame); + + /* Copy the status into EAX */ + TrapFrame->Eax = Status; + + /* Now exit the trap for real */ + KiExitTrap(TrapFrame, KTE_SKIP_SEG_BIT | KTE_SKIP_VOL_BIT); }
VOID @@ -1640,6 +1691,37 @@ KiDebugHandler(TrapFrame, TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx); }
+VOID +FASTCALL +NtContinueHandler(IN PCONTEXT Context, + IN BOOLEAN TestAlert) +{ + PKTHREAD Thread; + NTSTATUS Status; + PKTRAP_FRAME TrapFrame; + + /* Get trap frame and link previous one*/ + Thread = KeGetCurrentThread(); + TrapFrame = Thread->TrapFrame; + Thread->TrapFrame = (PKTRAP_FRAME)TrapFrame->Edx; + + /* Continue from this point on */ + Status = KiContinue(Context, NULL, TrapFrame); + if (NT_SUCCESS(Status)) + { + /* Check if alert was requested */ + if (TestAlert) KeTestAlertThread(Thread->PreviousMode); + + /* Exit to new trap frame */ + KiServiceExit2(TrapFrame); + } + else + { + /* Exit with an error */ + KiServiceExit(TrapFrame, Status); + } +} + /* HARDWARE INTERRUPTS ********************************************************/
/*