mirror of
https://github.com/reactos/reactos.git
synced 2025-08-05 19:42:57 +00:00
[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. svn path=/trunk/; revision=45142
This commit is contained in:
parent
aba18024bd
commit
e8437a07f6
4 changed files with 132 additions and 52 deletions
|
@ -932,6 +932,14 @@ KiEndUnexpectedRange(
|
||||||
VOID
|
VOID
|
||||||
);
|
);
|
||||||
|
|
||||||
|
NTSTATUS
|
||||||
|
NTAPI
|
||||||
|
KiContinue(
|
||||||
|
IN PCONTEXT Context,
|
||||||
|
IN PKEXCEPTION_FRAME ExceptionFrame,
|
||||||
|
IN PKTRAP_FRAME TrapFrame
|
||||||
|
);
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
FASTCALL
|
FASTCALL
|
||||||
KiServiceExit2(
|
KiServiceExit2(
|
||||||
|
|
|
@ -322,3 +322,35 @@ KiTrapReturn(IN PKTRAP_FRAME TrapFrame)
|
||||||
: "%esp"
|
: "%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"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -523,52 +523,10 @@ _NtRaiseException@12:
|
||||||
|
|
||||||
.func NtContinue@8
|
.func NtContinue@8
|
||||||
_NtContinue@8:
|
_NtContinue@8:
|
||||||
|
/* Call C code */
|
||||||
/* NOTE: We -must- be called by Zw* to have the right frame! */
|
mov ecx, [esp+4]
|
||||||
/* Push the stack frame */
|
mov edx, [esp+8]
|
||||||
push ebp
|
jmp @NtContinueHandler@8
|
||||||
|
|
||||||
/* 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
|
|
||||||
.endfunc
|
.endfunc
|
||||||
|
|
||||||
/* HARDWARE TRAP HANDLERS ****************************************************/
|
/* HARDWARE TRAP HANDLERS ****************************************************/
|
||||||
|
|
|
@ -54,6 +54,9 @@ KiExitTrap(IN PKTRAP_FRAME TrapFrame,
|
||||||
IN UCHAR Skip)
|
IN UCHAR Skip)
|
||||||
{
|
{
|
||||||
KTRAP_EXIT_SKIP_BITS SkipBits = { .Bits = Skip };
|
KTRAP_EXIT_SKIP_BITS SkipBits = { .Bits = Skip };
|
||||||
|
PULONG ReturnStack;
|
||||||
|
|
||||||
|
/* Debugging checks */
|
||||||
KiExitTrapDebugChecks(TrapFrame, SkipBits);
|
KiExitTrapDebugChecks(TrapFrame, SkipBits);
|
||||||
|
|
||||||
/* Restore the SEH handler chain */
|
/* Restore the SEH handler chain */
|
||||||
|
@ -82,11 +85,41 @@ KiExitTrap(IN PKTRAP_FRAME TrapFrame,
|
||||||
/* Check if the trap frame was edited */
|
/* Check if the trap frame was edited */
|
||||||
if (__builtin_expect(!(TrapFrame->SegCs & FRAME_EDITED), 0))
|
if (__builtin_expect(!(TrapFrame->SegCs & FRAME_EDITED), 0))
|
||||||
{
|
{
|
||||||
/* Not handled yet */
|
/*
|
||||||
UNIMPLEMENTED;
|
* An edited trap frame happens when we need to modify CS and/or ESP but
|
||||||
KiDumpTrapFrame(TrapFrame);
|
* don't actually have a ring transition. This happens when a kernelmode
|
||||||
DbgBreakPoint();
|
* caller wants to perform an NtContinue to another kernel address, such
|
||||||
while (TRUE);
|
* 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 */
|
/* Check if this is a user trap */
|
||||||
|
@ -214,6 +247,24 @@ KiEoiHelper(IN PKTRAP_FRAME TrapFrame)
|
||||||
KiExitTrap(TrapFrame, KTE_SKIP_PM_BIT);
|
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
|
VOID
|
||||||
FASTCALL
|
FASTCALL
|
||||||
KiServiceExit2(IN PKTRAP_FRAME TrapFrame)
|
KiServiceExit2(IN PKTRAP_FRAME TrapFrame)
|
||||||
|
@ -1640,6 +1691,37 @@ KiDebugServiceHandler(IN PKTRAP_FRAME TrapFrame)
|
||||||
KiDebugHandler(TrapFrame, TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx);
|
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 ********************************************************/
|
/* HARDWARE INTERRUPTS ********************************************************/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue