ReactOS  0.4.15-dev-1636-gf634010
usercall.c
Go to the documentation of this file.
1 /*
2  * PROJECT: ReactOS Kernel
3  * LICENSE: GPL - 2.0 + (https ://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE: AMD64 User-mode Callout Mechanisms (APC and Win32K Callbacks)
5  * COPYRIGHT: Timo Kreuzer(timo.kreuzer@reactos.org)
6  */
7 
8 /* INCLUDES ******************************************************************/
9 
10 #include <ntoskrnl.h>
11 #define NDEBUG
12 #include <debug.h>
13 
46 VOID
47 NTAPI
49  _In_ PKEXCEPTION_FRAME ExceptionFrame,
50  _Inout_ PKTRAP_FRAME TrapFrame,
51  _In_ PKNORMAL_ROUTINE NormalRoutine,
52  _In_ PVOID NormalContext,
55 {
57  EXCEPTION_RECORD ExceptionRecord;
58 
59  /* Sanity check, that the trap frame is from user mode */
60  ASSERT((TrapFrame->SegCs & MODE_MASK) != KernelMode);
61 
62  /* Align the user tack to 16 bytes and allocate space for a CONTEXT structure */
63  Context = (PCONTEXT)ALIGN_DOWN_POINTER_BY(TrapFrame->Rsp, 16) - 1;
64 
65  /* Protect with SEH */
66  _SEH2_TRY
67  {
68  /* Probe the context */
69  ProbeForWrite(Context, sizeof(CONTEXT), 16);
70 
71  /* Convert the current trap frame to a context */
73  KeTrapFrameToContext(TrapFrame, ExceptionFrame, Context);
74 
75  /* Set parameters for KiUserApcDispatcher */
76  Context->P1Home = (ULONG64)NormalContext;
77  Context->P2Home = (ULONG64)SystemArgument1;
78  Context->P3Home = (ULONG64)SystemArgument2;
79  Context->P4Home = (ULONG64)NormalRoutine;
80  }
81  _SEH2_EXCEPT(ExceptionRecord = *_SEH2_GetExceptionInformation()->ExceptionRecord, EXCEPTION_EXECUTE_HANDLER)
82  {
83  /* Dispatch the exception */
84  ExceptionRecord.ExceptionAddress = (PVOID)TrapFrame->Rip;
85  KiDispatchException(&ExceptionRecord,
86  ExceptionFrame,
87  TrapFrame,
88  UserMode,
89  TRUE);
90  }
91  _SEH2_END;
92 
93  /* Set the stack pointer to the context record */
94  TrapFrame->Rsp = (ULONG64)Context;
95 
96  /* We jump to KiUserApcDispatcher in ntdll */
97  TrapFrame->Rip = (ULONG64)KeUserApcDispatcher;
98 
99  /* Setup Ring 3 segments */
100  TrapFrame->SegCs = KGDT64_R3_CODE | RPL_MASK;
101  TrapFrame->SegFs = KGDT64_R3_CMTEB | RPL_MASK;
102  TrapFrame->SegGs = KGDT64_R3_DATA | RPL_MASK;
103  TrapFrame->SegSs = KGDT64_R3_DATA | RPL_MASK;
104 
105  /* Sanitize EFLAGS, enable interrupts */
106  TrapFrame->EFlags &= EFLAGS_USER_SANITIZE;
107  TrapFrame->EFlags |= EFLAGS_INTERRUPT_MASK;
108 }
109 
110 /*
111  * Stack layout for KiUserModeCallout:
112  * ----------------------------------
113  * KCALLOUT_FRAME.ResultLength <= 2nd Parameter to KiCallUserMode
114  * KCALLOUT_FRAME.Result <= 1st Parameter to KiCallUserMode
115  * KCALLOUT_FRAME.ReturnAddress <= Return address of KiCallUserMode
116  * KCALLOUT_FRAME.Ebp \
117  * KCALLOUT_FRAME.Ebx | = non-volatile registers, pushed
118  * KCALLOUT_FRAME.Esi | by KiCallUserMode
119  * KCALLOUT_FRAME.Edi /
120  * KCALLOUT_FRAME.CallbackStack
121  * KCALLOUT_FRAME.TrapFrame
122  * KCALLOUT_FRAME.InitialStack <= CalloutFrame points here
123  * ----------------------------------
124  * ~~ optional alignment ~~
125  * ----------------------------------
126  * FX_SAVE_AREA
127  * ----------------------------------
128  * KTRAP_FRAME
129  * ----------------------------------
130  * ~~ begin of stack frame for KiUserModeCallout ~~
131  *
132  */
133 NTSTATUS
134 FASTCALL
136  _Out_ PKCALLOUT_FRAME CalloutFrame)
137 {
138  PKTHREAD CurrentThread;
139  PKTRAP_FRAME TrapFrame;
140  KTRAP_FRAME CallbackTrapFrame;
141  PKIPCR Pcr;
142  ULONG_PTR InitialStack;
144 
145  /* Get the current thread */
146  CurrentThread = KeGetCurrentThread();
147 
148  /* Check if we are at pasive level */
150 
151  /* Check if we are attached or APCs are disabled */
152  ASSERT((CurrentThread->ApcStateIndex == OriginalApcEnvironment) &&
153  (CurrentThread->CombinedApcDisable == 0));
154 
155  /* Align stack on a 16-byte boundary */
156  InitialStack = (ULONG_PTR)ALIGN_DOWN_POINTER_BY(CalloutFrame, 16);
157 
158  /* Check if we have enough space on the stack */
159  if ((InitialStack - KERNEL_STACK_SIZE) < CurrentThread->StackLimit)
160  {
161  /* We don't, we'll have to grow our stack */
162  Status = MmGrowKernelStack((PVOID)InitialStack);
163 
164  /* Quit if we failed */
165  if (!NT_SUCCESS(Status)) return Status;
166  }
167 
168  /* Save the current callback stack and initial stack */
169  CalloutFrame->CallbackStack = (ULONG_PTR)CurrentThread->CallbackStack;
170  CalloutFrame->InitialStack = (ULONG_PTR)CurrentThread->InitialStack;
171 
172  /* Get and save the trap frame */
173  TrapFrame = CurrentThread->TrapFrame;
174  CalloutFrame->TrapFrame = (ULONG_PTR)TrapFrame;
175 
176  /* Set the new callback stack */
177  CurrentThread->CallbackStack = CalloutFrame;
178 
179  /* Disable interrupts so we can fill the NPX State */
180  _disable();
181 
182  /* Set the stack address */
183  CurrentThread->InitialStack = (PVOID)InitialStack;
184 
185  /* Copy the trap frame to the new location */
186  CallbackTrapFrame = *TrapFrame;
187 
188  /* Get PCR */
189  Pcr = (PKIPCR)KeGetPcr();
190 
191  /* Set user-mode dispatcher address as EIP */
192  Pcr->TssBase->Rsp0 = InitialStack;
193  Pcr->Prcb.RspBase = InitialStack;
194  CallbackTrapFrame.Rip = (ULONG_PTR)KeUserCallbackDispatcher;
195 
196  /* Bring interrupts back */
197  _enable();
198 
199  /* Exit to user-mode */
200  KiServiceExit(&CallbackTrapFrame, 0);
201 }
202 
203 VOID
205  _Out_ PUCALLOUT_FRAME UserCalloutFrame,
206  _In_ PKTRAP_FRAME TrapFrame,
207  _In_ ULONG ApiNumber,
208  _In_ PVOID Buffer,
210 {
211 #ifdef _M_IX86
212  CalloutFrame->Reserved = 0;
213  CalloutFrame->ApiNumber = ApiNumber;
214  CalloutFrame->Buffer = (ULONG_PTR)NewStack;
215  CalloutFrame->Length = ArgumentLength;
216 #elif defined(_M_AMD64)
217  UserCalloutFrame->Buffer = (PVOID)(UserCalloutFrame + 1);
218  UserCalloutFrame->Length = BufferLength;
219  UserCalloutFrame->ApiNumber = ApiNumber;
220  UserCalloutFrame->MachineFrame.Rip = TrapFrame->Rip;
221  UserCalloutFrame->MachineFrame.Rsp = TrapFrame->Rsp;
222 #else
223 #error "KiSetupUserCalloutFrame not implemented!"
224 #endif
225 }
226 
227 NTSTATUS
228 NTAPI
230  IN ULONG RoutineIndex,
231  IN PVOID Argument,
232  IN ULONG ArgumentLength,
233  OUT PVOID *Result,
235 {
236  ULONG_PTR OldStack;
237  PUCHAR UserArguments;
238  PUCALLOUT_FRAME CalloutFrame;
239  PULONG_PTR UserStackPointer;
241 #ifdef _M_IX86
242  PEXCEPTION_REGISTRATION_RECORD ExceptionList;
243 #endif // _M_IX86
244  PTEB Teb;
245  ULONG GdiBatchCount = 0;
246  ASSERT(KeGetCurrentThread()->ApcState.KernelApcInProgress == FALSE);
248 
249  /* Get the current user-mode stack */
250  UserStackPointer = KiGetUserModeStackAddress();
251  OldStack = *UserStackPointer;
252 
253  /* Enter a SEH Block */
254  _SEH2_TRY
255  {
256  /* Calculate and align the stack size */
257  UserArguments = (PUCHAR)ALIGN_DOWN_POINTER_BY(OldStack - ArgumentLength, sizeof(PVOID));
258 
259  /* The callout frame is below the arguments */
260  CalloutFrame = ((PUCALLOUT_FRAME)UserArguments) - 1;
261 
262  /* Make sure it's all writable */
263  ProbeForWrite(CalloutFrame,
264  sizeof(PUCALLOUT_FRAME) + ArgumentLength,
265  sizeof(PVOID));
266 
267  /* Copy the buffer into the stack */
268  RtlCopyMemory(UserArguments, Argument, ArgumentLength);
269 
270  /* Write the arguments */
271  KiSetupUserCalloutFrame(CalloutFrame,
272  KeGetCurrentThread()->TrapFrame,
273  RoutineIndex,
274  UserArguments,
275  ArgumentLength);
276 
277  /* Save the exception list */
278  Teb = KeGetCurrentThread()->Teb;
279 #ifdef _M_IX86
280  ExceptionList = Teb->NtTib.ExceptionList;
281 #endif // _M_IX86
282 
283  /* Jump to user mode */
284  *UserStackPointer = (ULONG_PTR)CalloutFrame;
287  {
288 #ifdef _M_IX86
289  /* Only restore the exception list if we didn't crash in ring 3 */
290  Teb->NtTib.ExceptionList = ExceptionList;
291 #endif // _M_IX86
292  }
293  else
294  {
295  /* Otherwise, pop the stack */
296  OldStack = *UserStackPointer;
297  }
298 
299  /* Read the GDI Batch count */
300  GdiBatchCount = Teb->GdiBatchCount;
301  }
303  {
304  /* Get the SEH exception */
306  }
307  _SEH2_END;
308 
309  /* Check if we have GDI Batch operations */
310  if (GdiBatchCount)
311  {
312  *UserStackPointer -= 256;
314  }
315 
316  /* Restore stack and return */
317  *UserStackPointer = OldStack;
318 #ifdef _M_AMD64 // could probably move the update to TrapFrame->Rsp from the C handler to the asm code
319  __writegsqword(FIELD_OFFSET(KIPCR, UserRsp), OldStack);
320 #endif
321  return CallbackStatus;
322 }
323 
324 NTSTATUS
325 NTAPI
327  _In_ PVOID Result,
330 {
331  PKTHREAD CurrentThread;
332  PKCALLOUT_FRAME CalloutFrame;
333  PKTRAP_FRAME CallbackTrapFrame, TrapFrame;
334  PKIPCR Pcr;
335 
336  /* Get the current thread and make sure we have a callback stack */
337  CurrentThread = KeGetCurrentThread();
338  CalloutFrame = CurrentThread->CallbackStack;
339  if (CalloutFrame == NULL)
340  {
342  }
343 
344  /* Store the results in the callback stack */
345  *((PVOID*)CalloutFrame->OutputBuffer) = Result;
346  *((ULONG*)CalloutFrame->OutputLength) = ResultLength;
347 
348  /* Get the trap frame */
349  CallbackTrapFrame = CurrentThread->TrapFrame;
350 
351  /* Disable interrupts for NPX save and stack switch */
352  _disable();
353 
354  /* Restore the exception list */
355  Pcr = (PKIPCR)KeGetPcr();
356 
357  /* Get the previous trap frame */
358  TrapFrame = (PKTRAP_FRAME)CalloutFrame->TrapFrame;
359 
360  /* Check if we failed in user mode */
362  {
363  *TrapFrame = *CallbackTrapFrame;
364  }
365 
366  /* Clear DR7 */
367  TrapFrame->Dr7 = 0;
368 
369  /* Check if debugging was active */
370  if (CurrentThread->Header.DebugActive & 0xFF)
371  {
372  /* Copy debug registers data from it */
373  TrapFrame->Dr0 = CallbackTrapFrame->Dr0;
374  TrapFrame->Dr1 = CallbackTrapFrame->Dr1;
375  TrapFrame->Dr2 = CallbackTrapFrame->Dr2;
376  TrapFrame->Dr3 = CallbackTrapFrame->Dr3;
377  TrapFrame->Dr6 = CallbackTrapFrame->Dr6;
378  TrapFrame->Dr7 = CallbackTrapFrame->Dr7;
379  }
380 
381  /* Switch the stack back to the previous value */
382  Pcr->TssBase->Rsp0 = CalloutFrame->InitialStack;
383  Pcr->Prcb.RspBase = CalloutFrame->InitialStack;
384 
385  /* Get the initial stack and restore it */
386  CurrentThread->InitialStack = (PVOID)CalloutFrame->InitialStack;
387 
388  /* Restore the trap frame and the previous callback stack */
389  CurrentThread->TrapFrame = TrapFrame;
390  CurrentThread->CallbackStack = (PVOID)CalloutFrame->CallbackStack;
391 
392  /* Bring interrupts back */
393  _enable();
394 
395  /* Now switch back to the old stack */
396  KiCallbackReturn(CalloutFrame, CallbackStatus);
397 }
398 
#define KeGetCurrentIrql()
Definition: env_spec_w32.h:706
#define KGDT64_R3_CODE
Definition: ketypes.h:76
#define IN
Definition: typedefs.h:39
_Must_inspect_result_ _In_ WDFDEVICE _In_ DEVICE_REGISTRY_PROPERTY _In_ ULONG BufferLength
Definition: wdfdevice.h:3767
#define MODE_MASK
Definition: orders.h:326
PVOID KeUserApcDispatcher
Definition: ke.h:137
ULONG CombinedApcDisable
Definition: ketypes.h:1815
#define STATUS_CALLBACK_POP_STACK
Definition: ntstatus.h:961
#define TRUE
Definition: types.h:120
unsigned char * PUCHAR
Definition: retypes.h:3
void __cdecl _enable(void)
Definition: intrin_arm.h:373
#define KeGetPreviousMode()
Definition: ketypes.h:1107
LONG NTSTATUS
Definition: precomp.h:26
struct _UCALLOUT_FRAME * PUCALLOUT_FRAME
struct _KIPCR * PKIPCR
#define KGDT64_R3_CMTEB
Definition: ketypes.h:78
PKTRAP_FRAME TrapFrame
Definition: ketypes.h:1706
ULONG64 TrapFrame
Definition: ketypes.h:948
ULONG64 OutputBuffer
Definition: ketypes.h:952
FORCEINLINE PULONG_PTR KiGetUserModeStackAddress(void)
Definition: ke.h:388
#define FASTCALL
Definition: nt_native.h:50
VOID NTAPI ProbeForWrite(IN PVOID Address, IN SIZE_T Length, IN ULONG Alignment)
Definition: exintrin.c:143
#define KeGetPcr()
Definition: ke.h:26
PVOID ExceptionAddress
Definition: compat.h:211
_SEH2_TRY
Definition: create.c:4226
struct _KTSS64 * TssBase
Definition: ketypes.h:861
uint32_t ULONG_PTR
Definition: typedefs.h:65
PVOID KeUserCallbackDispatcher
Definition: ke.h:138
UINT64 Dr2
Definition: ketypes.h:344
NTSTATUS(* NTAPI)(IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, OUT PULONG ErrorOffset)
Definition: IoEaTest.cpp:117
#define FALSE
Definition: types.h:117
VOID NTAPI KiDispatchException(PEXCEPTION_RECORD ExceptionRecord, PKEXCEPTION_FRAME ExceptionFrame, PKTRAP_FRAME Tf, KPROCESSOR_MODE PreviousMode, BOOLEAN SearchFrames)
#define KGDT64_R3_DATA
Definition: ketypes.h:75
#define EFLAGS_USER_SANITIZE
Definition: ketypes.h:133
#define _SEH2_GetExceptionInformation()
Definition: pseh2_64.h:11
#define EXCEPTION_EXECUTE_HANDLER
Definition: excpt.h:85
#define RPL_MASK
Definition: ketypes.h:69
_At_(*)(_In_ PWSK_CLIENT Client, _In_opt_ PUNICODE_STRING NodeName, _In_opt_ PUNICODE_STRING ServiceName, _In_opt_ ULONG NameSpace, _In_opt_ GUID *Provider, _In_opt_ PADDRINFOEXW Hints, _Outptr_ PADDRINFOEXW *Result, _In_opt_ PEPROCESS OwningProcess, _In_opt_ PETHREAD OwningThread, _Inout_ PIRP Irp Result)(Mem)) NTSTATUS(WSKAPI *PFN_WSK_GET_ADDRESS_INFO
Definition: wsk.h:426
#define _Out_
Definition: no_sal2.h:160
Definition: bufpool.h:45
VOID NTAPI KeTrapFrameToContext(IN PKTRAP_FRAME TrapFrame, IN PKEXCEPTION_FRAME ExceptionFrame, IN OUT PCONTEXT Context)
Definition: context.c:169
void * PVOID
Definition: retypes.h:9
NTSTATUS NTAPI MmGrowKernelStack(IN PVOID StackPointer)
Definition: procsup.c:459
NTSTATUS FASTCALL KiUserModeCallout(_Out_ PKCALLOUT_FRAME CalloutFrame)
Definition: usercall.c:135
DECLSPEC_NORETURN VOID FASTCALL KiServiceExit(IN PKTRAP_FRAME TrapFrame, IN NTSTATUS Status)
Definition: traphdlr.c:150
Status
Definition: gdiplustypes.h:24
_In_opt_ PVOID _In_opt_ PVOID SystemArgument1
Definition: ketypes.h:675
NTSTATUS NTAPI NtCallbackReturn(_In_ PVOID Result, _In_ ULONG ResultLength, _In_ NTSTATUS CallbackStatus)
Definition: usercall.c:326
PGDI_BATCHFLUSH_ROUTINE KeGdiFlushUserBatch
Definition: win32.c:20
#define CONTEXT_FULL
Definition: nt_native.h:1375
#define _SEH2_YIELD(STMT_)
Definition: pseh2_64.h:8
VOID NTAPI KiInitializeUserApc(_In_ PKEXCEPTION_FRAME ExceptionFrame, _Inout_ PKTRAP_FRAME TrapFrame, _In_ PKNORMAL_ROUTINE NormalRoutine, _In_ PVOID NormalContext, _In_ PVOID SystemArgument1, _In_ PVOID SystemArgument2)
Definition: usercall.c:48
#define ASSERT(a)
Definition: mode.c:45
if(!(yy_init))
Definition: macro.lex.yy.c:714
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
struct _EXCEPTION_REGISTRATION_RECORD * ExceptionList
Definition: compat.h:570
volatile VOID * StackLimit
Definition: ketypes.h:1597
UINT64 Dr6
Definition: ketypes.h:346
#define _Inout_
Definition: no_sal2.h:162
#define KERNEL_STACK_SIZE
UINT64 Dr3
Definition: ketypes.h:345
unsigned __int64 ULONG64
Definition: imports.h:198
_In_ FLT_PREOP_CALLBACK_STATUS CallbackStatus
Definition: fltkernel.h:1020
UINT64 Dr1
Definition: ketypes.h:343
ULONG GdiBatchCount
Definition: compat.h:746
KPRCB Prcb
Definition: ketypes.h:889
#define CONTEXT_DEBUG_REGISTERS
Definition: nt_native.h:1373
BOOLEAN DebugActive
Definition: ketypes.h:781
DECLSPEC_NORETURN VOID FASTCALL KiCallbackReturn(IN PVOID Stack, IN NTSTATUS Status)
#define PASSIVE_LEVEL
Definition: env_spec_w32.h:693
VOID KiSetupUserCalloutFrame(_Out_ PUCALLOUT_FRAME UserCalloutFrame, _In_ PKTRAP_FRAME TrapFrame, _In_ ULONG ApiNumber, _In_ PVOID Buffer, _In_ ULONG BufferLength)
Definition: usercall.c:204
UINT64 Dr0
Definition: ketypes.h:342
_In_opt_ PVOID _In_opt_ PVOID _In_opt_ PVOID SystemArgument2
Definition: ketypes.h:675
#define _In_
Definition: no_sal2.h:158
#define STATUS_NO_CALLBACK_ACTIVE
Definition: ntstatus.h:726
Definition: compat.h:694
_SEH2_END
Definition: create.c:4400
_Out_ PKAPC_STATE ApcState
Definition: mm.h:1471
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:255
unsigned int * PULONG
Definition: retypes.h:1
#define NULL
Definition: types.h:112
ULONG64 OutputLength
Definition: ketypes.h:953
DISPATCHER_HEADER Header
Definition: ketypes.h:1593
struct tagContext Context
Definition: acpixf.h:1034
UINT64 Rip
Definition: ketypes.h:379
#define OUT
Definition: typedefs.h:40
void __cdecl _disable(void)
Definition: intrin_arm.h:365
unsigned int ULONG
Definition: retypes.h:1
#define ULONG_PTR
Definition: config.h:101
uint32_t * PULONG_PTR
Definition: typedefs.h:65
#define RtlCopyMemory(Destination, Source, Length)
Definition: typedefs.h:263
_Must_inspect_result_ _In_ WDFDEVICE _In_ DEVICE_REGISTRY_PROPERTY _In_ ULONG _Out_ PULONG ResultLength
Definition: wdfdevice.h:3776
#define _SEH2_EXCEPT(...)
Definition: pseh2_64.h:6
UCHAR ApcStateIndex
Definition: ketypes.h:1874
struct _KTRAP_FRAME * PKTRAP_FRAME
#define _SEH2_GetExceptionCode()
Definition: pseh2_64.h:12
VOID(NTAPI * PKNORMAL_ROUTINE)(IN PVOID NormalContext OPTIONAL, IN PVOID SystemArgument1 OPTIONAL, IN PVOID SystemArgument2 OPTIONAL)
Definition: ketypes.h:674
UINT64 Dr7
Definition: ketypes.h:347
#define KeGetCurrentThread
Definition: hal.h:44
#define ALIGN_DOWN_POINTER_BY(ptr, align)
Definition: umtypes.h:82
PVOID InitialStack
Definition: ketypes.h:1596
NTSTATUS NTAPI KiCallUserMode(IN PVOID *OutputBuffer, IN PULONG OutputLength)
Definition: usercall.c:321
UINT64 RspBase
Definition: ketypes.h:576
NT_TIB NtTib
Definition: ntddk_ex.h:332
CONTEXT * PCONTEXT
Definition: compat.h:558
NTSTATUS NTAPI KeUserModeCallback(IN ULONG RoutineIndex, IN PVOID Argument, IN ULONG ArgumentLength, OUT PVOID *Result, OUT PULONG ResultLength)
Definition: usercall.c:229
UINT64 TrapFrame
Definition: ketypes.h:368
#define EFLAGS_INTERRUPT_MASK
Definition: ketypes.h:126