Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

653 lines
17 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. psctx.c
  5. Abstract:
  6. This procedure implements Get/Set Context Thread
  7. Author:
  8. Mark Lucovsky (markl) 25-May-1989
  9. Revision History:
  10. --*/
  11. #include "psp.h"
  12. VOID
  13. PspQueueApcSpecialApc(
  14. IN PKAPC Apc,
  15. IN PKNORMAL_ROUTINE *NormalRoutine,
  16. IN PVOID *NormalContext,
  17. IN PVOID *SystemArgument1,
  18. IN PVOID *SystemArgument2
  19. );
  20. #ifdef ALLOC_PRAGMA
  21. #pragma alloc_text(PAGE, NtGetContextThread)
  22. #pragma alloc_text(PAGE, NtSetContextThread)
  23. #pragma alloc_text(PAGE, PsGetContextThread)
  24. #pragma alloc_text(PAGE, PsSetContextThread)
  25. #pragma alloc_text(PAGE, NtQueueApcThread)
  26. #pragma alloc_text(PAGE, PspQueueApcSpecialApc)
  27. #endif
  28. VOID
  29. PspQueueApcSpecialApc(
  30. IN PKAPC Apc,
  31. IN PKNORMAL_ROUTINE *NormalRoutine,
  32. IN PVOID *NormalContext,
  33. IN PVOID *SystemArgument1,
  34. IN PVOID *SystemArgument2
  35. )
  36. {
  37. PAGED_CODE();
  38. UNREFERENCED_PARAMETER (NormalRoutine);
  39. UNREFERENCED_PARAMETER (NormalContext);
  40. UNREFERENCED_PARAMETER (SystemArgument1);
  41. UNREFERENCED_PARAMETER (SystemArgument2);
  42. ExFreePool(Apc);
  43. }
  44. NTSYSAPI
  45. NTSTATUS
  46. NTAPI
  47. NtQueueApcThread(
  48. IN HANDLE ThreadHandle,
  49. IN PPS_APC_ROUTINE ApcRoutine,
  50. IN PVOID ApcArgument1,
  51. IN PVOID ApcArgument2,
  52. IN PVOID ApcArgument3
  53. )
  54. /*++
  55. Routine Description:
  56. This function is used to queue a user-mode APC to the specified thread. The APC
  57. will fire when the specified thread does an alertable wait
  58. Arguments:
  59. ThreadHandle - Supplies a handle to a thread object. The caller
  60. must have THREAD_SET_CONTEXT access to the thread.
  61. ApcRoutine - Supplies the address of the APC routine to execute when the
  62. APC fires.
  63. ApcArgument1 - Supplies the first PVOID passed to the APC
  64. ApcArgument2 - Supplies the second PVOID passed to the APC
  65. ApcArgument3 - Supplies the third PVOID passed to the APC
  66. Return Value:
  67. Returns an NT Status code indicating success or failure of the API
  68. --*/
  69. {
  70. PETHREAD Thread;
  71. NTSTATUS st;
  72. KPROCESSOR_MODE Mode;
  73. PKAPC Apc;
  74. PAGED_CODE();
  75. Mode = KeGetPreviousMode ();
  76. st = ObReferenceObjectByHandle (ThreadHandle,
  77. THREAD_SET_CONTEXT,
  78. PsThreadType,
  79. Mode,
  80. &Thread,
  81. NULL);
  82. if (NT_SUCCESS (st)) {
  83. st = STATUS_SUCCESS;
  84. if (IS_SYSTEM_THREAD (Thread)) {
  85. st = STATUS_INVALID_HANDLE;
  86. } else {
  87. Apc = ExAllocatePoolWithQuotaTag (NonPagedPool | POOL_QUOTA_FAIL_INSTEAD_OF_RAISE,
  88. sizeof(*Apc),
  89. 'pasP');
  90. if (Apc == NULL) {
  91. st = STATUS_NO_MEMORY;
  92. } else {
  93. KeInitializeApc (Apc,
  94. &Thread->Tcb,
  95. OriginalApcEnvironment,
  96. PspQueueApcSpecialApc,
  97. NULL,
  98. (PKNORMAL_ROUTINE)ApcRoutine,
  99. UserMode,
  100. ApcArgument1);
  101. if (!KeInsertQueueApc (Apc, ApcArgument2, ApcArgument3, 0)) {
  102. ExFreePool (Apc);
  103. st = STATUS_UNSUCCESSFUL;
  104. }
  105. }
  106. }
  107. ObDereferenceObject (Thread);
  108. }
  109. return st;
  110. }
  111. NTSTATUS
  112. PsGetContextThread(
  113. IN PETHREAD Thread,
  114. IN OUT PCONTEXT ThreadContext,
  115. IN KPROCESSOR_MODE Mode
  116. )
  117. /*++
  118. Routine Description:
  119. This function returns the usermode context of the specified thread. This
  120. function will fail if the specified thread is a system thread. It will
  121. return the wrong answer if the thread is a non-system thread that does
  122. not execute in user-mode.
  123. Arguments:
  124. Thread - Supplies a pointer to the thread object from
  125. which to retrieve context information.
  126. ThreadContext - Supplies the address of a buffer that will receive
  127. the context of the specified thread.
  128. Mode - Mode to use for validation checks.
  129. Return Value:
  130. None.
  131. --*/
  132. {
  133. ULONG ContextFlags=0;
  134. GETSETCONTEXT ContextFrame = {0};
  135. ULONG ContextLength=0;
  136. KIRQL Irql;
  137. NTSTATUS Status;
  138. PETHREAD CurrentThread;
  139. PAGED_CODE();
  140. Status = STATUS_SUCCESS;
  141. //
  142. // Get previous mode and reference specified thread.
  143. //
  144. CurrentThread = PsGetCurrentThread ();
  145. //
  146. // Attempt to get the context of the specified thread.
  147. //
  148. try {
  149. //
  150. // Set the default alignment, capture the context flags,
  151. // and set the default size of the context record.
  152. //
  153. if (Mode != KernelMode) {
  154. ProbeForReadSmallStructure (ThreadContext,
  155. FIELD_OFFSET (CONTEXT, ContextFlags) + sizeof (ThreadContext->ContextFlags),
  156. CONTEXT_ALIGN);
  157. }
  158. ContextFlags = ThreadContext->ContextFlags;
  159. //
  160. // We don't need to re-probe here so long as the structure is smaller
  161. // than the guard region
  162. //
  163. ContextLength = sizeof(CONTEXT);
  164. ASSERT (ContextLength < 0x10000);
  165. #if defined(_X86_)
  166. //
  167. // CONTEXT_EXTENDED_REGISTERS is SET, then we want sizeof(CONTEXT) set above
  168. // otherwise (not set) we only want the old part of the context record.
  169. //
  170. if ((ContextFlags & CONTEXT_EXTENDED_REGISTERS) != CONTEXT_EXTENDED_REGISTERS) {
  171. ContextLength = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
  172. }
  173. #endif
  174. } except (EXCEPTION_EXECUTE_HANDLER) {
  175. return GetExceptionCode ();
  176. }
  177. KeInitializeEvent (&ContextFrame.OperationComplete,
  178. NotificationEvent,
  179. FALSE);
  180. ContextFrame.Context.ContextFlags = ContextFlags;
  181. ContextFrame.Mode = Mode;
  182. if (Thread == CurrentThread) {
  183. ContextFrame.Apc.SystemArgument1 = NULL;
  184. ContextFrame.Apc.SystemArgument2 = Thread;
  185. KeRaiseIrql (APC_LEVEL, &Irql);
  186. PspGetSetContextSpecialApc (&ContextFrame.Apc,
  187. NULL,
  188. NULL,
  189. &ContextFrame.Apc.SystemArgument1,
  190. &ContextFrame.Apc.SystemArgument2);
  191. KeLowerIrql (Irql);
  192. //
  193. // Move context to specfied context record. If an exception
  194. // occurs, then return the error.
  195. //
  196. try {
  197. RtlCopyMemory (ThreadContext,
  198. &ContextFrame.Context,
  199. ContextLength);
  200. } except (EXCEPTION_EXECUTE_HANDLER) {
  201. Status = GetExceptionCode ();
  202. }
  203. } else {
  204. KeInitializeApc (&ContextFrame.Apc,
  205. &Thread->Tcb,
  206. OriginalApcEnvironment,
  207. PspGetSetContextSpecialApc,
  208. NULL,
  209. NULL,
  210. KernelMode,
  211. NULL);
  212. if (!KeInsertQueueApc (&ContextFrame.Apc, NULL, Thread, 2)) {
  213. Status = STATUS_UNSUCCESSFUL;
  214. } else {
  215. KeWaitForSingleObject (&ContextFrame.OperationComplete,
  216. Executive,
  217. KernelMode,
  218. FALSE,
  219. NULL);
  220. //
  221. // Move context to specfied context record. If an
  222. // exception occurs, then silently handle it and
  223. // return success.
  224. //
  225. try {
  226. RtlCopyMemory (ThreadContext,
  227. &ContextFrame.Context,
  228. ContextLength);
  229. } except (EXCEPTION_EXECUTE_HANDLER) {
  230. Status = GetExceptionCode ();
  231. }
  232. }
  233. }
  234. return Status;
  235. }
  236. NTSTATUS
  237. NtGetContextThread(
  238. IN HANDLE ThreadHandle,
  239. IN OUT PCONTEXT ThreadContext
  240. )
  241. /*++
  242. Routine Description:
  243. This function returns the usermode context of the specified thread. This
  244. function will fail if the specified thread is a system thread. It will
  245. return the wrong answer if the thread is a non-system thread that does
  246. not execute in user-mode.
  247. Arguments:
  248. ThreadHandle - Supplies an open handle to the thread object from
  249. which to retrieve context information. The handle
  250. must allow THREAD_GET_CONTEXT access to the thread.
  251. ThreadContext - Supplies the address of a buffer that will receive
  252. the context of the specified thread.
  253. Return Value:
  254. None.
  255. --*/
  256. {
  257. KPROCESSOR_MODE Mode;
  258. NTSTATUS Status;
  259. PETHREAD Thread;
  260. PETHREAD CurrentThread;
  261. PAGED_CODE();
  262. //
  263. // Get previous mode and reference specified thread.
  264. //
  265. CurrentThread = PsGetCurrentThread ();
  266. Mode = KeGetPreviousModeByThread (&CurrentThread->Tcb);
  267. Status = ObReferenceObjectByHandle (ThreadHandle,
  268. THREAD_GET_CONTEXT,
  269. PsThreadType,
  270. Mode,
  271. &Thread,
  272. NULL);
  273. //
  274. // If the reference was successful, the check if the specified thread
  275. // is a system thread.
  276. //
  277. if (NT_SUCCESS (Status)) {
  278. //
  279. // If the thread is not a system thread, then attempt to get the
  280. // context of the thread.
  281. //
  282. if (IS_SYSTEM_THREAD (Thread) == FALSE) {
  283. Status = PsGetContextThread (Thread, ThreadContext, Mode);
  284. } else {
  285. Status = STATUS_INVALID_HANDLE;
  286. }
  287. ObDereferenceObject (Thread);
  288. }
  289. return Status;
  290. }
  291. NTSTATUS
  292. PsSetContextThread(
  293. IN PETHREAD Thread,
  294. IN PCONTEXT ThreadContext,
  295. IN KPROCESSOR_MODE Mode
  296. )
  297. /*++
  298. Routine Description:
  299. This function sets the usermode context of the specified thread. This
  300. function will fail if the specified thread is a system thread. It will
  301. return the wrong answer if the thread is a non-system thread that does
  302. not execute in user-mode.
  303. Arguments:
  304. Thread - Supplies the thread object from
  305. which to retrieve context information.
  306. ThreadContext - Supplies the address of a buffer that contains new
  307. context for the specified thread.
  308. Mode - Mode to use for validation checks.
  309. Return Value:
  310. None.
  311. --*/
  312. {
  313. ULONG ContextFlags=0;
  314. GETSETCONTEXT ContextFrame;
  315. ULONG ContextLength=0;
  316. KIRQL Irql;
  317. NTSTATUS Status;
  318. PETHREAD CurrentThread;
  319. PAGED_CODE();
  320. Status = STATUS_SUCCESS;
  321. //
  322. // Get previous mode and reference specified thread.
  323. //
  324. CurrentThread = PsGetCurrentThread ();
  325. //
  326. // Attempt to get the context of the specified thread.
  327. //
  328. try {
  329. //
  330. // Capture the context flags,
  331. // and set the default size of the context record.
  332. //
  333. if (Mode != KernelMode) {
  334. ProbeForReadSmallStructure (ThreadContext,
  335. FIELD_OFFSET (CONTEXT, ContextFlags) + sizeof (ThreadContext->ContextFlags),
  336. CONTEXT_ALIGN);
  337. }
  338. //
  339. // We don't need to re-probe here so long as the structure is small
  340. // enough not to cross the guard region.
  341. //
  342. ContextFlags = ThreadContext->ContextFlags;
  343. ContextLength = sizeof (CONTEXT);
  344. ASSERT (ContextLength < 0x10000);
  345. #if defined(_X86_)
  346. //
  347. // CONTEXT_EXTENDED_REGISTERS is SET, then we want sizeof(CONTEXT) set above
  348. // otherwise (not set) we only want the old part of the context record.
  349. //
  350. if ((ContextFlags & CONTEXT_EXTENDED_REGISTERS) != CONTEXT_EXTENDED_REGISTERS) {
  351. ContextLength = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
  352. }
  353. #endif
  354. RtlCopyMemory (&ContextFrame.Context, ThreadContext, ContextLength);
  355. } except (EXCEPTION_EXECUTE_HANDLER) {
  356. return GetExceptionCode ();
  357. }
  358. //
  359. // set the context of the target thread.
  360. //
  361. #if defined (_IA64_)
  362. //
  363. // On IA64 we need to fix up the PC if its a PLABEL address.
  364. //
  365. if (ContextFlags & CONTEXT_CONTROL) {
  366. PLABEL_DESCRIPTOR Label, *LabelAddress;
  367. SIZE_T BytesCopied;
  368. if (ContextFrame.Context.IntGp == 0) {
  369. LabelAddress = (PPLABEL_DESCRIPTOR)ContextFrame.Context.StIIP;
  370. try {
  371. //
  372. // We are in the wrong process here but it doesn't matter.
  373. // We just want to make sure this isn't a kernel address.
  374. //
  375. ProbeForReadSmallStructure (LabelAddress,
  376. sizeof (*LabelAddress),
  377. sizeof (ULONGLONG));
  378. } except (EXCEPTION_EXECUTE_HANDLER) {
  379. return GetExceptionCode ();
  380. }
  381. Status = MmCopyVirtualMemory (THREAD_TO_PROCESS (Thread),
  382. LabelAddress,
  383. PsGetCurrentProcessByThread (CurrentThread),
  384. &Label,
  385. sizeof (Label),
  386. KernelMode, // Needed to write to local stack
  387. &BytesCopied);
  388. if (NT_SUCCESS (Status)) {
  389. ContextFrame.Context.IntGp = Label.GlobalPointer;
  390. ContextFrame.Context.StIIP = Label.EntryPoint;
  391. ContextFrame.Context.StIPSR &= ~ISR_EI_MASK;
  392. } else {
  393. return Status;
  394. }
  395. }
  396. }
  397. #endif
  398. KeInitializeEvent (&ContextFrame.OperationComplete,
  399. NotificationEvent,
  400. FALSE);
  401. ContextFrame.Context.ContextFlags = ContextFlags;
  402. ContextFrame.Mode = Mode;
  403. if (Thread == CurrentThread) {
  404. ContextFrame.Apc.SystemArgument1 = (PVOID)1;
  405. ContextFrame.Apc.SystemArgument2 = Thread;
  406. KeRaiseIrql(APC_LEVEL, &Irql);
  407. PspGetSetContextSpecialApc (&ContextFrame.Apc,
  408. NULL,
  409. NULL,
  410. &ContextFrame.Apc.SystemArgument1,
  411. &ContextFrame.Apc.SystemArgument2);
  412. KeLowerIrql (Irql);
  413. } else {
  414. KeInitializeApc (&ContextFrame.Apc,
  415. &Thread->Tcb,
  416. OriginalApcEnvironment,
  417. PspGetSetContextSpecialApc,
  418. NULL,
  419. NULL,
  420. KernelMode,
  421. NULL);
  422. if (!KeInsertQueueApc (&ContextFrame.Apc, (PVOID)1, Thread, 2)) {
  423. Status = STATUS_UNSUCCESSFUL;
  424. } else {
  425. KeWaitForSingleObject (&ContextFrame.OperationComplete,
  426. Executive,
  427. KernelMode,
  428. FALSE,
  429. NULL);
  430. }
  431. }
  432. return Status;
  433. }
  434. NTSTATUS
  435. NtSetContextThread(
  436. IN HANDLE ThreadHandle,
  437. IN PCONTEXT ThreadContext
  438. )
  439. /*++
  440. Routine Description:
  441. This function sets the usermode context of the specified thread. This
  442. function will fail if the specified thread is a system thread. It will
  443. return the wrong answer if the thread is a non-system thread that does
  444. not execute in user-mode.
  445. Arguments:
  446. ThreadHandle - Supplies an open handle to the thread object from
  447. which to retrieve context information. The handle
  448. must allow THREAD_SET_CONTEXT access to the thread.
  449. ThreadContext - Supplies the address of a buffer that contains new
  450. context for the specified thread.
  451. Return Value:
  452. None.
  453. --*/
  454. {
  455. KPROCESSOR_MODE Mode;
  456. NTSTATUS Status;
  457. PETHREAD Thread;
  458. PETHREAD CurrentThread;
  459. PAGED_CODE();
  460. //
  461. // Get previous mode and reference specified thread.
  462. //
  463. CurrentThread = PsGetCurrentThread ();
  464. Mode = KeGetPreviousModeByThread (&CurrentThread->Tcb);
  465. Status = ObReferenceObjectByHandle (ThreadHandle,
  466. THREAD_SET_CONTEXT,
  467. PsThreadType,
  468. Mode,
  469. &Thread,
  470. NULL);
  471. //
  472. // If the reference was successful, the check if the specified thread
  473. // is a system thread.
  474. //
  475. if (NT_SUCCESS (Status)) {
  476. //
  477. // If the thread is not a system thread, then attempt to get the
  478. // context of the thread.
  479. //
  480. if (IS_SYSTEM_THREAD (Thread) == FALSE) {
  481. Status = PsSetContextThread (Thread, ThreadContext, Mode);
  482. } else {
  483. Status = STATUS_INVALID_HANDLE;
  484. }
  485. ObDereferenceObject (Thread);
  486. }
  487. return Status;
  488. }