[NTOS:KE:X64][ASM:X64] Fix delivery of APCs

- Deliver pending APCs on trap exit
- Pass the trapframe of KiApcInterrupt to KiDeliverApcs, not NULL.
- Fix parameter passing from KiSwapContext to KiSwapContextInternal and KiSwapContextResume, so that the ApcBypass parameter is not uninitialized
- Fix return value of KiSwapContextResume to correctly indicate whether we want to have APCs directly delivered or not (when there are non, or when delivery is suppressed)
This commit is contained in:
Timo Kreuzer 2018-02-12 20:53:15 +01:00
parent fdc1261fb7
commit a016ccd117
4 changed files with 76 additions and 35 deletions

View file

@ -11,6 +11,11 @@
#include <ksamd64.inc> #include <ksamd64.inc>
; BOOLEAN
; KiSwapContextResume(
; _In_ KIRQL WaitIrql,
; _In_ PKTHREAD OldThread,
; _In_ PKTHREAD NewThread)
EXTERN KiSwapContextResume:PROC EXTERN KiSwapContextResume:PROC
/* FUNCTIONS ****************************************************************/ /* FUNCTIONS ****************************************************************/
@ -118,23 +123,22 @@ PUBLIC KiThreadStartup
* \brief * \brief
* The KiSwapContextInternal routine switches context to another thread. * The KiSwapContextInternal routine switches context to another thread.
* *
* \param rcx * \param cl
* Pointer to the KTHREAD to which the caller wishes to switch to. * The IRQL at wich the old thread is suspended
* *
* \param rdx * \param rdx
* Pointer to the KTHREAD to which the caller wishes to switch from. * Pointer to the KTHREAD to which the caller wishes to switch from.
* *
* \param r8b * \param r8
* APC bypass * Pointer to the KTHREAD to which the caller wishes to switch to.
* *
* \return * \return
* None. * The WaitStatus of the Target Thread.
* *
* \remarks * \remarks
* ... * ...
* *
*--*/ *--*/
PUBLIC KiSwapContextInternal
.PROC KiSwapContextInternal .PROC KiSwapContextInternal
push rbp push rbp
@ -143,22 +147,17 @@ PUBLIC KiSwapContextInternal
.allocstack 6 * 8 .allocstack 6 * 8
.endprolog .endprolog
/* Save APC bypass */ /* Save WaitIrql as KSWITCH_FRAME::ApcBypass */
mov [rsp + SwApcBypass], r8b mov [rsp + SwApcBypass], cl
/* Save kernel stack of old thread */ /* Save kernel stack of old thread */
mov [rdx + KTHREAD_KernelStack], rsp mov [rdx + KTHREAD_KernelStack], rsp
/* Save new thread in rbp */
mov rbp, rcx
//call KiSwapContextSuspend
/* Load stack of new thread */ /* Load stack of new thread */
mov rsp, [rbp + KTHREAD_KernelStack] mov rsp, [r8 + KTHREAD_KernelStack]
/* Reload APC bypass */ /* Reload APC bypass */
mov r8b, [rsp + SwApcBypass] mov cl, [rsp + SwApcBypass]
call KiSwapContextResume call KiSwapContextResume
@ -178,13 +177,13 @@ PUBLIC KiSwapContextInternal
* The KiSwapContext routine switches context to another thread. * The KiSwapContext routine switches context to another thread.
* *
* BOOLEAN * BOOLEAN
* KiSwapContext(KIRQL WaitIrql, PKTHREAD CurrentThread); * KiSwapContext(KIRQL WaitIrql, PKTHREAD OldThread);
* *
* \param WaitIrql <rcx> * \param WaitIrql <cl>
* ... * The IRQL at wich the old thread is suspended
* *
* \param CurrentThread <rdx> * \param OldThread <rdx>
* Pointer to the KTHREAD of the current thread. * Pointer to the KTHREAD of the previous thread.
* *
* \return * \return
* The WaitStatus of the Target Thread. * The WaitStatus of the Target Thread.
@ -205,7 +204,7 @@ PUBLIC KiSwapContext
GENERATE_EXCEPTION_FRAME GENERATE_EXCEPTION_FRAME
/* Do the swap with the registers correctly setup */ /* Do the swap with the registers correctly setup */
mov rcx, gs:[PcCurrentThread] /* Pointer to the new thread */ mov r8, gs:[PcCurrentThread] /* Pointer to the new thread */
call KiSwapContextInternal call KiSwapContextInternal
/* Restore the registers from the KEXCEPTION_FRAME */ /* Restore the registers from the KEXCEPTION_FRAME */

View file

@ -137,9 +137,9 @@ KiInitializeContextThread(IN PKTHREAD Thread,
BOOLEAN BOOLEAN
KiSwapContextResume( KiSwapContextResume(
IN PKTHREAD NewThread, _In_ BOOLEAN ApcBypass,
IN PKTHREAD OldThread, _In_ PKTHREAD OldThread,
IN BOOLEAN ApcBypass) _In_ PKTHREAD NewThread)
{ {
PKIPCR Pcr = (PKIPCR)KeGetPcr(); PKIPCR Pcr = (PKIPCR)KeGetPcr();
PKPROCESS OldProcess, NewProcess; PKPROCESS OldProcess, NewProcess;
@ -187,14 +187,15 @@ KiSwapContextResume(
if (NewThread->ApcState.KernelApcPending) if (NewThread->ApcState.KernelApcPending)
{ {
/* Are APCs enabled? */ /* Are APCs enabled? */
if (!NewThread->SpecialApcDisable) if ((NewThread->SpecialApcDisable == 0) &&
(ApcBypass == 0))
{ {
/* Request APC delivery */ /* Return TRUE to indicate that we want APCs to be delivered */
if (!ApcBypass)
HalRequestSoftwareInterrupt(APC_LEVEL);
else
return TRUE; return TRUE;
} }
/* Request an APC interrupt to be delivered later */
HalRequestSoftwareInterrupt(APC_LEVEL);
} }
/* Return stating that no kernel APCs are pending*/ /* Return stating that no kernel APCs are pending*/

View file

@ -592,7 +592,7 @@ PUBLIC KiApcInterrupt
mov cl, [rbp + KTRAP_FRAME_SegCs] // ProcessorMode mov cl, [rbp + KTRAP_FRAME_SegCs] // ProcessorMode
and cl, 1 and cl, 1
mov rdx, 0 // ExceptionFrame mov rdx, 0 // ExceptionFrame
mov r8, rdx // TrapFrame mov r8, rbp // TrapFrame
call KiDeliverApc call KiDeliverApc
/* Disable interrupts */ /* Disable interrupts */
@ -1000,8 +1000,47 @@ KiConvertToGuiThreadFailed:
ENDFUNC ENDFUNC
KiExitToUserApc: ;
int 3 ; VOID
; KiDeliverApc(
; _In_ KPROCESSOR_MODE DeliveryMode,
; _In_ PKEXCEPTION_FRAME ExceptionFrame,
; _In_ PKTRAP_FRAME TrapFrame);
;
EXTERN KiDeliverApc:PROC
PUBLIC KiInitiateUserApc
.PROC KiInitiateUserApc
; Generate a KEXCEPTION_FRAME on the stack
GENERATE_EXCEPTION_FRAME
; Raise IRQL to APC_LEVEL
mov rax, APC_LEVEL
mov cr8, rax
; Enable interrupts
sti
; Get the current trap frame
mov rax, gs:[PcCurrentThread]
mov r8, [rax + KTHREAD_TrapFrame]
; Call the C function
mov ecx, 1
mov rdx, rsp
call KiDeliverApc
; Disable interrupts again
cli
; Restore the registers from the KEXCEPTION_FRAME
RESTORE_EXCEPTION_STATE
; Return
ret
.ENDP
PUBLIC KiInitializeSegments PUBLIC KiInitializeSegments

View file

@ -150,6 +150,7 @@ ENDM
MACRO(ExitTrap, Flags) MACRO(ExitTrap, Flags)
LOCAL kernel_mode_return LOCAL kernel_mode_return
LOCAL IntsEnabled LOCAL IntsEnabled
LOCAL NoUserApc
#if DBG #if DBG
/* Check previous irql */ /* Check previous irql */
@ -181,8 +182,9 @@ MACRO(ExitTrap, Flags)
/* Load current thread into r10 */ /* Load current thread into r10 */
mov r10, gs:[PcCurrentThread] mov r10, gs:[PcCurrentThread]
cmp byte ptr [r10 + KTHREAD_UserApcPending], 0 cmp byte ptr [r10 + KTHREAD_UserApcPending], 0
jne KiExitToUserApc je NoUserApc
call KiInitiateUserApc
NoUserApc:
endif endif
#if DBG #if DBG