Thread/Process Termination/Repeaing Rewrite + Fixes
[reactos.git] / reactos / ntoskrnl / ps / query.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ps/query.c
5 * PURPOSE: Set/Query Process/Thread Information APIs
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) - Created File
8 * David Welch
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* GLOBALS ******************************************************************/
18
19
20 static const INFORMATION_CLASS_INFO PsProcessInfoClass[] =
21 {
22 ICI_SQ_SAME( sizeof(PROCESS_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* ProcessBasicInformation */
23 ICI_SQ_SAME( sizeof(QUOTA_LIMITS), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessQuotaLimits */
24 ICI_SQ_SAME( sizeof(IO_COUNTERS), sizeof(ULONG), ICIF_QUERY ), /* ProcessIoCounters */
25 ICI_SQ_SAME( sizeof(VM_COUNTERS), sizeof(ULONG), ICIF_QUERY ), /* ProcessVmCounters */
26 ICI_SQ_SAME( sizeof(KERNEL_USER_TIMES), sizeof(ULONG), ICIF_QUERY ), /* ProcessTimes */
27 ICI_SQ_SAME( sizeof(KPRIORITY), sizeof(ULONG), ICIF_SET ), /* ProcessBasePriority */
28 ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_SET ), /* ProcessRaisePriority */
29 ICI_SQ_SAME( sizeof(HANDLE), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessDebugPort */
30 ICI_SQ_SAME( sizeof(HANDLE), sizeof(ULONG), ICIF_SET ), /* ProcessExceptionPort */
31 ICI_SQ_SAME( sizeof(PROCESS_ACCESS_TOKEN), sizeof(ULONG), ICIF_SET ), /* ProcessAccessToken */
32 ICI_SQ_SAME( 0 /* FIXME */, sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessLdtInformation */
33 ICI_SQ_SAME( 0 /* FIXME */, sizeof(ULONG), ICIF_SET ), /* ProcessLdtSize */
34 ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessDefaultHardErrorMode */
35 ICI_SQ_SAME( 0 /* FIXME */, sizeof(ULONG), ICIF_SET ), /* ProcessIoPortHandlers */
36 ICI_SQ_SAME( sizeof(POOLED_USAGE_AND_LIMITS), sizeof(ULONG), ICIF_QUERY ), /* ProcessPooledUsageAndLimits */
37 ICI_SQ_SAME( sizeof(PROCESS_WS_WATCH_INFORMATION), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessWorkingSetWatch */
38 ICI_SQ_SAME( 0 /* FIXME */, sizeof(ULONG), ICIF_SET ), /* ProcessUserModeIOPL */
39 ICI_SQ_SAME( sizeof(BOOLEAN), sizeof(ULONG), ICIF_SET ), /* ProcessEnableAlignmentFaultFixup */
40 ICI_SQ_SAME( sizeof(PROCESS_PRIORITY_CLASS), sizeof(USHORT), ICIF_QUERY | ICIF_SET ), /* ProcessPriorityClass */
41 ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY ), /* ProcessWx86Information */
42 ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY ), /* ProcessHandleCount */
43 ICI_SQ_SAME( sizeof(KAFFINITY), sizeof(ULONG), ICIF_SET ), /* ProcessAffinityMask */
44 ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessPriorityBoost */
45
46 ICI_SQ(/*Q*/ sizeof(((PPROCESS_DEVICEMAP_INFORMATION)0x0)->Query), /* ProcessDeviceMap */
47 /*S*/ sizeof(((PPROCESS_DEVICEMAP_INFORMATION)0x0)->Set),
48 /*Q*/ sizeof(ULONG),
49 /*S*/ sizeof(ULONG),
50 ICIF_QUERY | ICIF_SET ),
51
52 ICI_SQ_SAME( sizeof(PROCESS_SESSION_INFORMATION), sizeof(ULONG), ICIF_QUERY | ICIF_SET ), /* ProcessSessionInformation */
53 ICI_SQ_SAME( sizeof(BOOLEAN), sizeof(ULONG), ICIF_SET ), /* ProcessForegroundInformation */
54 ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY ), /* ProcessWow64Information */
55 ICI_SQ_SAME( sizeof(UNICODE_STRING), sizeof(ULONG), ICIF_QUERY | ICIF_SIZE_VARIABLE), /* ProcessImageFileName */
56
57 /* FIXME */
58 ICI_SQ_SAME( 0, 1, 0 ), /* ProcessLUIDDeviceMapsEnabled */
59 ICI_SQ_SAME( 0, 1, 0 ), /* ProcessBreakOnTermination */
60 ICI_SQ_SAME( 0, 1, 0 ), /* ProcessDebugObjectHandle */
61 ICI_SQ_SAME( 0, 1, 0 ), /* ProcessDebugFlags */
62 ICI_SQ_SAME( 0, 1, 0 ), /* ProcessHandleTracing */
63 ICI_SQ_SAME( 0, 1, 0 ), /* ProcessUnknown33 */
64 ICI_SQ_SAME( 0, 1, 0 ), /* ProcessUnknown34 */
65 ICI_SQ_SAME( 0, 1, 0 ), /* ProcessUnknown35 */
66
67 ICI_SQ_SAME( sizeof(ULONG), sizeof(ULONG), ICIF_QUERY), /* ProcessCookie */
68 };
69
70 /*
71 * FIXME:
72 * Remove the Implemented value if all functions are implemented.
73 */
74
75 static const struct
76 {
77 BOOLEAN Implemented;
78 ULONG Size;
79 } QueryInformationData[MaxThreadInfoClass + 1] =
80 {
81 {TRUE, sizeof(THREAD_BASIC_INFORMATION)}, // ThreadBasicInformation
82 {TRUE, sizeof(KERNEL_USER_TIMES)}, // ThreadTimes
83 {TRUE, 0}, // ThreadPriority
84 {TRUE, 0}, // ThreadBasePriority
85 {TRUE, 0}, // ThreadAffinityMask
86 {TRUE, 0}, // ThreadImpersonationToken
87 {FALSE, 0}, // ThreadDescriptorTableEntry
88 {TRUE, 0}, // ThreadEnableAlignmentFaultFixup
89 {TRUE, 0}, // ThreadEventPair
90 {TRUE, sizeof(PVOID)}, // ThreadQuerySetWin32StartAddress
91 {TRUE, 0}, // ThreadZeroTlsCell
92 {TRUE, sizeof(LARGE_INTEGER)}, // ThreadPerformanceCount
93 {TRUE, sizeof(BOOLEAN)}, // ThreadAmILastThread
94 {TRUE, 0}, // ThreadIdealProcessor
95 {FALSE, 0}, // ThreadPriorityBoost
96 {TRUE, 0}, // ThreadSetTlsArrayAddress
97 {FALSE, 0}, // ThreadIsIoPending
98 {TRUE, 0} // ThreadHideFromDebugger
99 };
100
101 static const struct
102 {
103 BOOLEAN Implemented;
104 ULONG Size;
105 } SetInformationData[MaxThreadInfoClass + 1] =
106 {
107 {TRUE, 0}, // ThreadBasicInformation
108 {TRUE, 0}, // ThreadTimes
109 {TRUE, sizeof(KPRIORITY)}, // ThreadPriority
110 {TRUE, sizeof(LONG)}, // ThreadBasePriority
111 {TRUE, sizeof(KAFFINITY)}, // ThreadAffinityMask
112 {TRUE, sizeof(HANDLE)}, // ThreadImpersonationToken
113 {TRUE, 0}, // ThreadDescriptorTableEntry
114 {FALSE, 0}, // ThreadEnableAlignmentFaultFixup
115 {FALSE, 0}, // ThreadEventPair
116 {TRUE, sizeof(PVOID)}, // ThreadQuerySetWin32StartAddress
117 {FALSE, 0}, // ThreadZeroTlsCell
118 {TRUE, 0}, // ThreadPerformanceCount
119 {TRUE, 0}, // ThreadAmILastThread
120 {FALSE, 0}, // ThreadIdealProcessor
121 {FALSE, 0}, // ThreadPriorityBoost
122 {FALSE, 0}, // ThreadSetTlsArrayAddress
123 {TRUE, 0}, // ThreadIsIoPending
124 {FALSE, 0} // ThreadHideFromDebugger
125 };
126
127 /* FUNCTIONS *****************************************************************/
128
129 /*
130 * @unimplemented
131 */
132 NTSTATUS STDCALL
133 NtQueryInformationProcess(IN HANDLE ProcessHandle,
134 IN PROCESSINFOCLASS ProcessInformationClass,
135 OUT PVOID ProcessInformation,
136 IN ULONG ProcessInformationLength,
137 OUT PULONG ReturnLength OPTIONAL)
138 {
139 PEPROCESS Process;
140 KPROCESSOR_MODE PreviousMode;
141 NTSTATUS Status = STATUS_SUCCESS;
142
143 PAGED_CODE();
144
145 PreviousMode = ExGetPreviousMode();
146
147 DefaultQueryInfoBufferCheck(ProcessInformationClass,
148 PsProcessInfoClass,
149 ProcessInformation,
150 ProcessInformationLength,
151 ReturnLength,
152 PreviousMode,
153 &Status);
154 if(!NT_SUCCESS(Status))
155 {
156 DPRINT1("NtQueryInformationProcess() failed, Status: 0x%x\n", Status);
157 return Status;
158 }
159
160 if(ProcessInformationClass != ProcessCookie)
161 {
162 Status = ObReferenceObjectByHandle(ProcessHandle,
163 PROCESS_QUERY_INFORMATION,
164 PsProcessType,
165 PreviousMode,
166 (PVOID*)&Process,
167 NULL);
168 if (!NT_SUCCESS(Status))
169 {
170 return(Status);
171 }
172 }
173 else if(ProcessHandle != NtCurrentProcess())
174 {
175 /* retreiving the process cookie is only allowed for the calling process
176 itself! XP only allowes NtCurrentProcess() as process handles even if a
177 real handle actually represents the current process. */
178 return STATUS_INVALID_PARAMETER;
179 }
180
181 switch (ProcessInformationClass)
182 {
183 case ProcessBasicInformation:
184 {
185 PPROCESS_BASIC_INFORMATION ProcessBasicInformationP =
186 (PPROCESS_BASIC_INFORMATION)ProcessInformation;
187
188 _SEH_TRY
189 {
190 ProcessBasicInformationP->ExitStatus = Process->ExitStatus;
191 ProcessBasicInformationP->PebBaseAddress = Process->Peb;
192 ProcessBasicInformationP->AffinityMask = Process->Pcb.Affinity;
193 ProcessBasicInformationP->UniqueProcessId =
194 Process->UniqueProcessId;
195 ProcessBasicInformationP->InheritedFromUniqueProcessId =
196 Process->InheritedFromUniqueProcessId;
197 ProcessBasicInformationP->BasePriority =
198 Process->Pcb.BasePriority;
199
200 if (ReturnLength)
201 {
202 *ReturnLength = sizeof(PROCESS_BASIC_INFORMATION);
203 }
204 }
205 _SEH_HANDLE
206 {
207 Status = _SEH_GetExceptionCode();
208 }
209 _SEH_END;
210 break;
211 }
212
213 case ProcessQuotaLimits:
214 case ProcessIoCounters:
215 Status = STATUS_NOT_IMPLEMENTED;
216 break;
217
218 case ProcessTimes:
219 {
220 PKERNEL_USER_TIMES ProcessTimeP = (PKERNEL_USER_TIMES)ProcessInformation;
221 _SEH_TRY
222 {
223 ProcessTimeP->CreateTime = Process->CreateTime;
224 ProcessTimeP->UserTime.QuadPart = Process->Pcb.UserTime * 100000LL;
225 ProcessTimeP->KernelTime.QuadPart = Process->Pcb.KernelTime * 100000LL;
226 ProcessTimeP->ExitTime = Process->ExitTime;
227
228 if (ReturnLength)
229 {
230 *ReturnLength = sizeof(KERNEL_USER_TIMES);
231 }
232 }
233 _SEH_HANDLE
234 {
235 Status = _SEH_GetExceptionCode();
236 }
237 _SEH_END;
238 break;
239 }
240
241 case ProcessDebugPort:
242 {
243 _SEH_TRY
244 {
245 *(PHANDLE)ProcessInformation = (Process->DebugPort != NULL ? (HANDLE)-1 : NULL);
246 if (ReturnLength)
247 {
248 *ReturnLength = sizeof(HANDLE);
249 }
250 }
251 _SEH_HANDLE
252 {
253 Status = _SEH_GetExceptionCode();
254 }
255 _SEH_END;
256 break;
257 }
258
259 case ProcessLdtInformation:
260 case ProcessWorkingSetWatch:
261 case ProcessWx86Information:
262 Status = STATUS_NOT_IMPLEMENTED;
263 break;
264
265 case ProcessHandleCount:
266 {
267 ULONG HandleCount = ObpGetHandleCountByHandleTable(Process->ObjectTable);
268
269 _SEH_TRY
270 {
271 *(PULONG)ProcessInformation = HandleCount;
272 if (ReturnLength)
273 {
274 *ReturnLength = sizeof(ULONG);
275 }
276 }
277 _SEH_HANDLE
278 {
279 Status = _SEH_GetExceptionCode();
280 }
281 _SEH_END;
282 break;
283 }
284
285 case ProcessSessionInformation:
286 {
287 PPROCESS_SESSION_INFORMATION SessionInfo = (PPROCESS_SESSION_INFORMATION)ProcessInformation;
288
289 _SEH_TRY
290 {
291 SessionInfo->SessionId = Process->SessionId;
292 if (ReturnLength)
293 {
294 *ReturnLength = sizeof(PROCESS_SESSION_INFORMATION);
295 }
296 }
297 _SEH_HANDLE
298 {
299 Status = _SEH_GetExceptionCode();
300 }
301 _SEH_END;
302 break;
303 }
304
305 case ProcessWow64Information:
306 DPRINT1("We currently don't support the ProcessWow64Information information class!\n");
307 Status = STATUS_NOT_IMPLEMENTED;
308 break;
309
310 case ProcessVmCounters:
311 {
312 PVM_COUNTERS pOut = (PVM_COUNTERS)ProcessInformation;
313
314 _SEH_TRY
315 {
316 pOut->PeakVirtualSize = Process->PeakVirtualSize;
317 /*
318 * Here we should probably use VirtualSize.LowPart, but due to
319 * incompatibilities in current headers (no unnamed union),
320 * I opted for cast.
321 */
322 pOut->VirtualSize = (ULONG)Process->VirtualSize.QuadPart;
323 pOut->PageFaultCount = Process->Vm.PageFaultCount;
324 pOut->PeakWorkingSetSize = Process->Vm.PeakWorkingSetSize;
325 pOut->WorkingSetSize = Process->Vm.WorkingSetSize;
326 pOut->QuotaPeakPagedPoolUsage = Process->QuotaPeakPoolUsage[0]; // TODO: Verify!
327 pOut->QuotaPagedPoolUsage = Process->QuotaPoolUsage[0]; // TODO: Verify!
328 pOut->QuotaPeakNonPagedPoolUsage = Process->QuotaPeakPoolUsage[1]; // TODO: Verify!
329 pOut->QuotaNonPagedPoolUsage = Process->QuotaPoolUsage[1]; // TODO: Verify!
330 pOut->PagefileUsage = Process->PagefileUsage;
331 pOut->PeakPagefileUsage = Process->PeakPagefileUsage;
332
333 if (ReturnLength)
334 {
335 *ReturnLength = sizeof(VM_COUNTERS);
336 }
337 }
338 _SEH_HANDLE
339 {
340 Status = _SEH_GetExceptionCode();
341 }
342 _SEH_END;
343 break;
344 }
345
346 case ProcessDefaultHardErrorMode:
347 {
348 PULONG HardErrMode = (PULONG)ProcessInformation;
349 _SEH_TRY
350 {
351 *HardErrMode = Process->DefaultHardErrorProcessing;
352 if (ReturnLength)
353 {
354 *ReturnLength = sizeof(ULONG);
355 }
356 }
357 _SEH_HANDLE
358 {
359 Status = _SEH_GetExceptionCode();
360 }
361 _SEH_END;
362 break;
363 }
364
365 case ProcessPriorityBoost:
366 {
367 PULONG BoostEnabled = (PULONG)ProcessInformation;
368
369 _SEH_TRY
370 {
371 *BoostEnabled = Process->Pcb.DisableBoost ? FALSE : TRUE;
372
373 if (ReturnLength)
374 {
375 *ReturnLength = sizeof(ULONG);
376 }
377 }
378 _SEH_HANDLE
379 {
380 Status = _SEH_GetExceptionCode();
381 }
382 _SEH_END;
383 break;
384 }
385
386 case ProcessDeviceMap:
387 {
388 PROCESS_DEVICEMAP_INFORMATION DeviceMap;
389
390 ObQueryDeviceMapInformation(Process, &DeviceMap);
391
392 _SEH_TRY
393 {
394 *(PPROCESS_DEVICEMAP_INFORMATION)ProcessInformation = DeviceMap;
395 if (ReturnLength)
396 {
397 *ReturnLength = sizeof(PROCESS_DEVICEMAP_INFORMATION);
398 }
399 }
400 _SEH_HANDLE
401 {
402 Status = _SEH_GetExceptionCode();
403 }
404 _SEH_END;
405 break;
406 }
407
408 case ProcessPriorityClass:
409 {
410 PUSHORT Priority = (PUSHORT)ProcessInformation;
411
412 _SEH_TRY
413 {
414 *Priority = Process->PriorityClass;
415
416 if (ReturnLength)
417 {
418 *ReturnLength = sizeof(USHORT);
419 }
420 }
421 _SEH_HANDLE
422 {
423 Status = _SEH_GetExceptionCode();
424 }
425 _SEH_END;
426 break;
427 }
428
429 case ProcessImageFileName:
430 {
431 /*
432 * We DO NOT return the file name stored in the EPROCESS structure.
433 * Propably if we can't find a PEB or ProcessParameters structure for the
434 * process!
435 */
436 if(Process->Peb != NULL)
437 {
438 PRTL_USER_PROCESS_PARAMETERS ProcParams = NULL;
439 UNICODE_STRING LocalDest;
440 BOOLEAN Attached;
441 ULONG ImagePathLen = 0;
442 PUNICODE_STRING DstPath = (PUNICODE_STRING)ProcessInformation;
443
444 /* we need to attach to the process to make sure we're in the right context! */
445 Attached = Process != PsGetCurrentProcess();
446
447 if(Attached)
448 KeAttachProcess(&Process->Pcb);
449
450 _SEH_TRY
451 {
452 ProcParams = Process->Peb->ProcessParameters;
453 ImagePathLen = ProcParams->ImagePathName.Length;
454 }
455 _SEH_HANDLE
456 {
457 Status = _SEH_GetExceptionCode();
458 }
459 _SEH_END;
460
461 if(NT_SUCCESS(Status))
462 {
463 if(ProcessInformationLength < sizeof(UNICODE_STRING) + ImagePathLen + sizeof(WCHAR))
464 {
465 Status = STATUS_INFO_LENGTH_MISMATCH;
466 }
467 else
468 {
469 PWSTR StrSource = NULL;
470
471 /* create a DstPath structure on the stack */
472 _SEH_TRY
473 {
474 LocalDest.Length = ImagePathLen;
475 LocalDest.MaximumLength = ImagePathLen + sizeof(WCHAR);
476 LocalDest.Buffer = (PWSTR)(DstPath + 1);
477
478 /* save a copy of the pointer to the source buffer */
479 StrSource = ProcParams->ImagePathName.Buffer;
480 }
481 _SEH_HANDLE
482 {
483 Status = _SEH_GetExceptionCode();
484 }
485 _SEH_END;
486
487 if(NT_SUCCESS(Status))
488 {
489 /* now, let's allocate some anonymous memory to copy the string to.
490 we can't just copy it to the buffer the caller pointed as it might
491 be user memory in another context */
492 PWSTR PathCopy = ExAllocatePool(PagedPool, LocalDest.Length + sizeof(WCHAR));
493 if(PathCopy != NULL)
494 {
495 /* make a copy of the buffer to the temporary buffer */
496 _SEH_TRY
497 {
498 RtlCopyMemory(PathCopy, StrSource, LocalDest.Length);
499 PathCopy[LocalDest.Length / sizeof(WCHAR)] = L'\0';
500 }
501 _SEH_HANDLE
502 {
503 Status = _SEH_GetExceptionCode();
504 }
505 _SEH_END;
506
507 /* detach from the process */
508 if(Attached)
509 KeDetachProcess();
510
511 /* only copy the string back to the caller if we were able to
512 copy it into the temporary buffer! */
513 if(NT_SUCCESS(Status))
514 {
515 /* now let's copy the buffer back to the caller */
516 _SEH_TRY
517 {
518 *DstPath = LocalDest;
519 RtlCopyMemory(LocalDest.Buffer, PathCopy, LocalDest.Length + sizeof(WCHAR));
520 if (ReturnLength)
521 {
522 *ReturnLength = sizeof(UNICODE_STRING) + LocalDest.Length + sizeof(WCHAR);
523 }
524 }
525 _SEH_HANDLE
526 {
527 Status = _SEH_GetExceptionCode();
528 }
529 _SEH_END;
530 }
531
532 /* we're done with the copy operation, free the temporary kernel buffer */
533 ExFreePool(PathCopy);
534
535 /* we need to bail because we're already detached from the process */
536 break;
537 }
538 else
539 {
540 Status = STATUS_INSUFFICIENT_RESOURCES;
541 }
542 }
543 }
544 }
545
546 /* don't forget to detach from the process!!! */
547 if(Attached)
548 KeDetachProcess();
549 }
550 else
551 {
552 /* FIXME - what to do here? */
553 Status = STATUS_UNSUCCESSFUL;
554 }
555 break;
556 }
557
558 case ProcessCookie:
559 {
560 ULONG Cookie;
561
562 /* receive the process cookie, this is only allowed for the current
563 process! */
564
565 Process = PsGetCurrentProcess();
566
567 Cookie = Process->Cookie;
568 if(Cookie == 0)
569 {
570 LARGE_INTEGER SystemTime;
571 ULONG NewCookie;
572 PKPRCB Prcb;
573
574 /* generate a new cookie */
575
576 KeQuerySystemTime(&SystemTime);
577
578 Prcb = KeGetCurrentPrcb();
579
580 NewCookie = Prcb->KeSystemCalls ^ Prcb->InterruptTime ^
581 SystemTime.u.LowPart ^ SystemTime.u.HighPart;
582
583 /* try to set the new cookie, return the current one if another thread
584 set it in the meanwhile */
585 Cookie = InterlockedCompareExchange((LONG*)&Process->Cookie,
586 NewCookie,
587 Cookie);
588 if(Cookie == 0)
589 {
590 /* successfully set the cookie */
591 Cookie = NewCookie;
592 }
593 }
594
595 _SEH_TRY
596 {
597 *(PULONG)ProcessInformation = Cookie;
598 if (ReturnLength)
599 {
600 *ReturnLength = sizeof(ULONG);
601 }
602 }
603 _SEH_HANDLE
604 {
605 Status = _SEH_GetExceptionCode();
606 }
607 _SEH_END;
608
609 break;
610 }
611
612 /*
613 * Note: The following 10 information classes are verified to not be
614 * implemented on NT, and do indeed return STATUS_INVALID_INFO_CLASS;
615 */
616 case ProcessBasePriority:
617 case ProcessRaisePriority:
618 case ProcessExceptionPort:
619 case ProcessAccessToken:
620 case ProcessLdtSize:
621 case ProcessIoPortHandlers:
622 case ProcessUserModeIOPL:
623 case ProcessEnableAlignmentFaultFixup:
624 case ProcessAffinityMask:
625 case ProcessForegroundInformation:
626 default:
627 Status = STATUS_INVALID_INFO_CLASS;
628 }
629
630 if(ProcessInformationClass != ProcessCookie)
631 {
632 ObDereferenceObject(Process);
633 }
634
635 return Status;
636 }
637
638 /*
639 * @unimplemented
640 */
641 NTSTATUS STDCALL
642 NtSetInformationProcess(IN HANDLE ProcessHandle,
643 IN PROCESSINFOCLASS ProcessInformationClass,
644 IN PVOID ProcessInformation,
645 IN ULONG ProcessInformationLength)
646 {
647 PEPROCESS Process;
648 KPROCESSOR_MODE PreviousMode;
649 ACCESS_MASK Access;
650 NTSTATUS Status = STATUS_SUCCESS;
651
652 PAGED_CODE();
653
654 PreviousMode = ExGetPreviousMode();
655
656 DefaultSetInfoBufferCheck(ProcessInformationClass,
657 PsProcessInfoClass,
658 ProcessInformation,
659 ProcessInformationLength,
660 PreviousMode,
661 &Status);
662 if(!NT_SUCCESS(Status))
663 {
664 DPRINT1("NtSetInformationProcess() %d %x %x called\n", ProcessInformationClass, ProcessInformation, ProcessInformationLength);
665 DPRINT1("NtSetInformationProcess() %x failed, Status: 0x%x\n", Status);
666 return Status;
667 }
668
669 switch(ProcessInformationClass)
670 {
671 case ProcessSessionInformation:
672 Access = PROCESS_SET_INFORMATION | PROCESS_SET_SESSIONID;
673 break;
674 case ProcessExceptionPort:
675 case ProcessDebugPort:
676 Access = PROCESS_SET_INFORMATION | PROCESS_SET_PORT;
677 break;
678
679 default:
680 Access = PROCESS_SET_INFORMATION;
681 break;
682 }
683
684 Status = ObReferenceObjectByHandle(ProcessHandle,
685 Access,
686 PsProcessType,
687 PreviousMode,
688 (PVOID*)&Process,
689 NULL);
690 if (!NT_SUCCESS(Status))
691 {
692 return(Status);
693 }
694
695 switch (ProcessInformationClass)
696 {
697 case ProcessQuotaLimits:
698 case ProcessBasePriority:
699 case ProcessRaisePriority:
700 Status = STATUS_NOT_IMPLEMENTED;
701 break;
702
703 case ProcessDebugPort:
704 {
705 HANDLE PortHandle = NULL;
706
707 /* make a safe copy of the buffer on the stack */
708 _SEH_TRY
709 {
710 PortHandle = *(PHANDLE)ProcessInformation;
711 Status = (PortHandle != NULL ? STATUS_SUCCESS : STATUS_INVALID_PARAMETER);
712 }
713 _SEH_HANDLE
714 {
715 Status = _SEH_GetExceptionCode();
716 }
717 _SEH_END;
718
719 if(NT_SUCCESS(Status))
720 {
721 PEPORT DebugPort;
722
723 /* in case we had success reading from the buffer, verify the provided
724 * LPC port handle
725 */
726 Status = ObReferenceObjectByHandle(PortHandle,
727 0,
728 LpcPortObjectType,
729 PreviousMode,
730 (PVOID)&DebugPort,
731 NULL);
732 if(NT_SUCCESS(Status))
733 {
734 /* lock the process to be thread-safe! */
735
736 Status = PsLockProcess(Process, FALSE);
737 if(NT_SUCCESS(Status))
738 {
739 /*
740 * according to "NT Native API" documentation, setting the debug
741 * port is only permitted once!
742 */
743 if(Process->DebugPort == NULL)
744 {
745 /* keep the reference to the handle! */
746 Process->DebugPort = DebugPort;
747
748 if(Process->Peb)
749 {
750 /* we're now debugging the process, so set the flag in the PEB
751 structure. However, to access it we need to attach to the
752 process so we're sure we're in the right context! */
753
754 KeAttachProcess(&Process->Pcb);
755 _SEH_TRY
756 {
757 Process->Peb->BeingDebugged = TRUE;
758 }
759 _SEH_HANDLE
760 {
761 DPRINT1("Trying to set the Peb->BeingDebugged field of process 0x%x failed, exception: 0x%x\n", Process, _SEH_GetExceptionCode());
762 }
763 _SEH_END;
764 KeDetachProcess();
765 }
766 Status = STATUS_SUCCESS;
767 }
768 else
769 {
770 ObDereferenceObject(DebugPort);
771 Status = STATUS_PORT_ALREADY_SET;
772 }
773 PsUnlockProcess(Process);
774 }
775 else
776 {
777 ObDereferenceObject(DebugPort);
778 }
779 }
780 }
781 break;
782 }
783
784 case ProcessExceptionPort:
785 {
786 HANDLE PortHandle = NULL;
787
788 /* make a safe copy of the buffer on the stack */
789 _SEH_TRY
790 {
791 PortHandle = *(PHANDLE)ProcessInformation;
792 Status = STATUS_SUCCESS;
793 }
794 _SEH_HANDLE
795 {
796 Status = _SEH_GetExceptionCode();
797 }
798 _SEH_END;
799
800 if(NT_SUCCESS(Status))
801 {
802 PEPORT ExceptionPort;
803
804 /* in case we had success reading from the buffer, verify the provided
805 * LPC port handle
806 */
807 Status = ObReferenceObjectByHandle(PortHandle,
808 0,
809 LpcPortObjectType,
810 PreviousMode,
811 (PVOID)&ExceptionPort,
812 NULL);
813 if(NT_SUCCESS(Status))
814 {
815 /* lock the process to be thread-safe! */
816
817 Status = PsLockProcess(Process, FALSE);
818 if(NT_SUCCESS(Status))
819 {
820 /*
821 * according to "NT Native API" documentation, setting the exception
822 * port is only permitted once!
823 */
824 if(Process->ExceptionPort == NULL)
825 {
826 /* keep the reference to the handle! */
827 Process->ExceptionPort = ExceptionPort;
828 Status = STATUS_SUCCESS;
829 }
830 else
831 {
832 ObDereferenceObject(ExceptionPort);
833 Status = STATUS_PORT_ALREADY_SET;
834 }
835 PsUnlockProcess(Process);
836 }
837 else
838 {
839 ObDereferenceObject(ExceptionPort);
840 }
841 }
842 }
843 break;
844 }
845
846 case ProcessAccessToken:
847 {
848 HANDLE TokenHandle = NULL;
849
850 /* make a safe copy of the buffer on the stack */
851 _SEH_TRY
852 {
853 TokenHandle = ((PPROCESS_ACCESS_TOKEN)ProcessInformation)->Token;
854 Status = STATUS_SUCCESS;
855 }
856 _SEH_HANDLE
857 {
858 Status = _SEH_GetExceptionCode();
859 }
860 _SEH_END;
861
862 if(NT_SUCCESS(Status))
863 {
864 /* in case we had success reading from the buffer, perform the actual task */
865 Status = PspAssignPrimaryToken(Process, TokenHandle);
866 }
867 break;
868 }
869
870 case ProcessDefaultHardErrorMode:
871 {
872 _SEH_TRY
873 {
874 InterlockedExchange((LONG*)&Process->DefaultHardErrorProcessing,
875 *(PLONG)ProcessInformation);
876 Status = STATUS_SUCCESS;
877 }
878 _SEH_HANDLE
879 {
880 Status = _SEH_GetExceptionCode();
881 }
882 _SEH_END;
883 break;
884 }
885
886 case ProcessSessionInformation:
887 {
888 PROCESS_SESSION_INFORMATION SessionInfo;
889 Status = STATUS_SUCCESS;
890
891 _SEH_TRY
892 {
893 /* copy the structure to the stack */
894 SessionInfo = *(PPROCESS_SESSION_INFORMATION)ProcessInformation;
895 }
896 _SEH_HANDLE
897 {
898 Status = _SEH_GetExceptionCode();
899 }
900 _SEH_END;
901
902 if(NT_SUCCESS(Status))
903 {
904 /* we successfully copied the structure to the stack, continue processing */
905
906 /*
907 * setting the session id requires the SeTcbPrivilege!
908 */
909 if(!SeSinglePrivilegeCheck(SeTcbPrivilege,
910 PreviousMode))
911 {
912 DPRINT1("NtSetInformationProcess: Caller requires the SeTcbPrivilege privilege for setting ProcessSessionInformation!\n");
913 /* can't set the session id, bail! */
914 Status = STATUS_PRIVILEGE_NOT_HELD;
915 break;
916 }
917
918 /* FIXME - update the session id for the process token */
919
920 Status = PsLockProcess(Process, FALSE);
921 if(NT_SUCCESS(Status))
922 {
923 Process->SessionId = SessionInfo.SessionId;
924
925 /* Update the session id in the PEB structure */
926 if(Process->Peb != NULL)
927 {
928 /* we need to attach to the process to make sure we're in the right
929 context to access the PEB structure */
930 KeAttachProcess(&Process->Pcb);
931
932 _SEH_TRY
933 {
934 /* FIXME: Process->Peb->SessionId = SessionInfo.SessionId; */
935
936 Status = STATUS_SUCCESS;
937 }
938 _SEH_HANDLE
939 {
940 Status = _SEH_GetExceptionCode();
941 }
942 _SEH_END;
943
944 KeDetachProcess();
945 }
946
947 PsUnlockProcess(Process);
948 }
949 }
950 break;
951 }
952
953 case ProcessPriorityClass:
954 {
955 PROCESS_PRIORITY_CLASS ppc;
956
957 _SEH_TRY
958 {
959 ppc = *(PPROCESS_PRIORITY_CLASS)ProcessInformation;
960 }
961 _SEH_HANDLE
962 {
963 Status = _SEH_GetExceptionCode();
964 }
965 _SEH_END;
966
967 if(NT_SUCCESS(Status))
968 {
969 }
970
971 break;
972 }
973
974 case ProcessLdtInformation:
975 case ProcessLdtSize:
976 case ProcessIoPortHandlers:
977 case ProcessWorkingSetWatch:
978 case ProcessUserModeIOPL:
979 case ProcessEnableAlignmentFaultFixup:
980 case ProcessAffinityMask:
981 Status = STATUS_NOT_IMPLEMENTED;
982 break;
983
984 case ProcessBasicInformation:
985 case ProcessIoCounters:
986 case ProcessTimes:
987 case ProcessPooledUsageAndLimits:
988 case ProcessWx86Information:
989 case ProcessHandleCount:
990 case ProcessWow64Information:
991 default:
992 Status = STATUS_INVALID_INFO_CLASS;
993 }
994 ObDereferenceObject(Process);
995 return(Status);
996 }
997
998
999 /**********************************************************************
1000 * NAME INTERNAL
1001 * PiQuerySystemProcessInformation
1002 *
1003 * DESCRIPTION
1004 * Compute the size of a process+thread snapshot as
1005 * expected by NtQuerySystemInformation.
1006 *
1007 * RETURN VALUE
1008 * 0 on error; otherwise the size, in bytes of the buffer
1009 * required to write a full snapshot.
1010 *
1011 * NOTE
1012 * We assume (sizeof (PVOID) == sizeof (ULONG)) holds.
1013 */
1014 NTSTATUS
1015 PiQuerySystemProcessInformation(PVOID Buffer,
1016 ULONG Size,
1017 PULONG ReqSize)
1018 {
1019 return STATUS_NOT_IMPLEMENTED;
1020
1021 #if 0
1022 PLIST_ENTRY CurrentEntryP;
1023 PEPROCESS CurrentP;
1024 PLIST_ENTRY CurrentEntryT;
1025 PETHREAD CurrentT;
1026
1027 ULONG RequiredSize = 0L;
1028 BOOLEAN SizeOnly = FALSE;
1029
1030 ULONG SpiSize = 0L;
1031
1032 PSYSTEM_PROCESS_INFORMATION pInfoP = (PSYSTEM_PROCESS_INFORMATION) SnapshotBuffer;
1033 PSYSTEM_PROCESS_INFORMATION pInfoPLast = NULL;
1034 PSYSTEM_THREAD_INFO pInfoT = NULL;
1035
1036
1037 /* Lock the process list. */
1038 ExAcquireFastMutex(&PspActiveProcessMutex);
1039
1040 /*
1041 * Scan the process list. Since the
1042 * list is circular, the guard is false
1043 * after the last process.
1044 */
1045 for ( CurrentEntryP = PsActiveProcessHead.Flink;
1046 (CurrentEntryP != & PsActiveProcessHead);
1047 CurrentEntryP = CurrentEntryP->Flink
1048 )
1049 {
1050 /*
1051 * Compute how much space is
1052 * occupied in the snapshot
1053 * by adding this process info.
1054 * (at least one thread).
1055 */
1056 SpiSizeCurrent = sizeof (SYSTEM_PROCESS_INFORMATION);
1057 RequiredSize += SpiSizeCurrent;
1058 /*
1059 * Do not write process data in the
1060 * buffer if it is too small.
1061 */
1062 if (TRUE == SizeOnly) continue;
1063 /*
1064 * Check if the buffer can contain
1065 * the full snapshot.
1066 */
1067 if (Size < RequiredSize)
1068 {
1069 SizeOnly = TRUE;
1070 continue;
1071 }
1072 /*
1073 * Get a reference to the
1074 * process descriptor we are
1075 * handling.
1076 */
1077 CurrentP = CONTAINING_RECORD(
1078 CurrentEntryP,
1079 EPROCESS,
1080 ProcessListEntry
1081 );
1082 /*
1083 * Write process data in the buffer.
1084 */
1085 RtlZeroMemory (pInfoP, sizeof (SYSTEM_PROCESS_INFORMATION));
1086 /* PROCESS */
1087 pInfoP->ThreadCount = 0L;
1088 pInfoP->ProcessId = CurrentP->UniqueProcessId;
1089 RtlInitUnicodeString (
1090 & pInfoP->Name,
1091 CurrentP->ImageFileName
1092 );
1093 /* THREAD */
1094 for ( pInfoT = & CurrentP->ThreadSysInfo [0],
1095 CurrentEntryT = CurrentP->ThreadListHead.Flink;
1096
1097 (CurrentEntryT != & CurrentP->ThreadListHead);
1098
1099 pInfoT = & CurrentP->ThreadSysInfo [pInfoP->ThreadCount],
1100 CurrentEntryT = CurrentEntryT->Flink
1101 )
1102 {
1103 /*
1104 * Recalculate the size of the
1105 * information block.
1106 */
1107 if (0 < pInfoP->ThreadCount)
1108 {
1109 RequiredSize += sizeof (SYSTEM_THREAD_INFORMATION);
1110 }
1111 /*
1112 * Do not write thread data in the
1113 * buffer if it is too small.
1114 */
1115 if (TRUE == SizeOnly) continue;
1116 /*
1117 * Check if the buffer can contain
1118 * the full snapshot.
1119 */
1120 if (Size < RequiredSize)
1121 {
1122 SizeOnly = TRUE;
1123 continue;
1124 }
1125 /*
1126 * Get a reference to the
1127 * thread descriptor we are
1128 * handling.
1129 */
1130 CurrentT = CONTAINING_RECORD(
1131 CurrentEntryT,
1132 KTHREAD,
1133 ThreadListEntry
1134 );
1135 /*
1136 * Write thread data.
1137 */
1138 RtlZeroMemory (
1139 pInfoT,
1140 sizeof (SYSTEM_THREAD_INFORMATION)
1141 );
1142 pInfoT->KernelTime = CurrentT-> ; /* TIME */
1143 pInfoT->UserTime = CurrentT-> ; /* TIME */
1144 pInfoT->CreateTime = CurrentT-> ; /* TIME */
1145 pInfoT->TickCount = CurrentT-> ; /* ULONG */
1146 pInfoT->StartEIP = CurrentT-> ; /* ULONG */
1147 pInfoT->ClientId = CurrentT-> ; /* CLIENT_ID */
1148 pInfoT->ClientId = CurrentT-> ; /* CLIENT_ID */
1149 pInfoT->DynamicPriority = CurrentT-> ; /* ULONG */
1150 pInfoT->BasePriority = CurrentT-> ; /* ULONG */
1151 pInfoT->nSwitches = CurrentT-> ; /* ULONG */
1152 pInfoT->State = CurrentT-> ; /* DWORD */
1153 pInfoT->WaitReason = CurrentT-> ; /* KWAIT_REASON */
1154 /*
1155 * Count the number of threads
1156 * this process has.
1157 */
1158 ++ pInfoP->ThreadCount;
1159 }
1160 /*
1161 * Save the size of information
1162 * stored in the buffer for the
1163 * current process.
1164 */
1165 pInfoP->RelativeOffset = SpiSize;
1166 /*
1167 * Save a reference to the last
1168 * valid information block.
1169 */
1170 pInfoPLast = pInfoP;
1171 /*
1172 * Compute the offset of the
1173 * SYSTEM_PROCESS_INFORMATION
1174 * descriptor in the snapshot
1175 * buffer for the next process.
1176 */
1177 (ULONG) pInfoP += SpiSize;
1178 }
1179 /*
1180 * Unlock the process list.
1181 */
1182 ExReleaseFastMutex (
1183 & PspActiveProcessMutex
1184 );
1185 /*
1186 * Return the proper error status code,
1187 * if the buffer was too small.
1188 */
1189 if (TRUE == SizeOnly)
1190 {
1191 if (NULL != RequiredSize)
1192 {
1193 *pRequiredSize = RequiredSize;
1194 }
1195 return STATUS_INFO_LENGTH_MISMATCH;
1196 }
1197 /*
1198 * Mark the end of the snapshot.
1199 */
1200 pInfoP->RelativeOffset = 0L;
1201 /* OK */
1202 return STATUS_SUCCESS;
1203 #endif
1204 }
1205
1206 /*
1207 * @unimplemented
1208 */
1209 NTSTATUS STDCALL
1210 NtSetInformationThread (IN HANDLE ThreadHandle,
1211 IN THREADINFOCLASS ThreadInformationClass,
1212 IN PVOID ThreadInformation,
1213 IN ULONG ThreadInformationLength)
1214 {
1215 PETHREAD Thread;
1216 NTSTATUS Status;
1217 union
1218 {
1219 KPRIORITY Priority;
1220 LONG Increment;
1221 KAFFINITY Affinity;
1222 HANDLE Handle;
1223 PVOID Address;
1224 }u;
1225
1226 PAGED_CODE();
1227
1228 if (ThreadInformationClass <= MaxThreadInfoClass &&
1229 !SetInformationData[ThreadInformationClass].Implemented)
1230 {
1231 return STATUS_NOT_IMPLEMENTED;
1232 }
1233 if (ThreadInformationClass > MaxThreadInfoClass ||
1234 SetInformationData[ThreadInformationClass].Size == 0)
1235 {
1236 return STATUS_INVALID_INFO_CLASS;
1237 }
1238 if (ThreadInformationLength != SetInformationData[ThreadInformationClass].Size)
1239 {
1240 return STATUS_INFO_LENGTH_MISMATCH;
1241 }
1242
1243 Status = ObReferenceObjectByHandle (ThreadHandle,
1244 THREAD_SET_INFORMATION,
1245 PsThreadType,
1246 ExGetPreviousMode (),
1247 (PVOID*)&Thread,
1248 NULL);
1249 if (!NT_SUCCESS(Status))
1250 {
1251 return Status;
1252 }
1253
1254 Status = MmCopyFromCaller(&u.Priority,
1255 ThreadInformation,
1256 SetInformationData[ThreadInformationClass].Size);
1257 if (NT_SUCCESS(Status))
1258 {
1259 switch (ThreadInformationClass)
1260 {
1261 case ThreadPriority:
1262 if (u.Priority < LOW_PRIORITY || u.Priority >= MAXIMUM_PRIORITY)
1263 {
1264 Status = STATUS_INVALID_PARAMETER;
1265 break;
1266 }
1267 KeSetPriorityThread(&Thread->Tcb, u.Priority);
1268 break;
1269
1270 case ThreadBasePriority:
1271 KeSetBasePriorityThread (&Thread->Tcb, u.Increment);
1272 break;
1273
1274 case ThreadAffinityMask:
1275 Status = KeSetAffinityThread(&Thread->Tcb, u.Affinity);
1276 break;
1277
1278 case ThreadImpersonationToken:
1279 Status = PsAssignImpersonationToken (Thread, u.Handle);
1280 break;
1281
1282 case ThreadQuerySetWin32StartAddress:
1283 Thread->Win32StartAddress = u.Address;
1284 break;
1285
1286 default:
1287 /* Shoult never occure if the data table is correct */
1288 KEBUGCHECK(0);
1289 }
1290 }
1291 ObDereferenceObject (Thread);
1292
1293 return Status;
1294 }
1295
1296 /*
1297 * @implemented
1298 */
1299 NTSTATUS STDCALL
1300 NtQueryInformationThread (IN HANDLE ThreadHandle,
1301 IN THREADINFOCLASS ThreadInformationClass,
1302 OUT PVOID ThreadInformation,
1303 IN ULONG ThreadInformationLength,
1304 OUT PULONG ReturnLength OPTIONAL)
1305 {
1306 PETHREAD Thread;
1307 NTSTATUS Status;
1308 union
1309 {
1310 THREAD_BASIC_INFORMATION TBI;
1311 KERNEL_USER_TIMES TTI;
1312 PVOID Address;
1313 LARGE_INTEGER Count;
1314 BOOLEAN Last;
1315 }u;
1316
1317 PAGED_CODE();
1318
1319 if (ThreadInformationClass <= MaxThreadInfoClass &&
1320 !QueryInformationData[ThreadInformationClass].Implemented)
1321 {
1322 return STATUS_NOT_IMPLEMENTED;
1323 }
1324 if (ThreadInformationClass > MaxThreadInfoClass ||
1325 QueryInformationData[ThreadInformationClass].Size == 0)
1326 {
1327 return STATUS_INVALID_INFO_CLASS;
1328 }
1329 if (ThreadInformationLength != QueryInformationData[ThreadInformationClass].Size)
1330 {
1331 return STATUS_INFO_LENGTH_MISMATCH;
1332 }
1333
1334 Status = ObReferenceObjectByHandle(ThreadHandle,
1335 THREAD_QUERY_INFORMATION,
1336 PsThreadType,
1337 ExGetPreviousMode(),
1338 (PVOID*)&Thread,
1339 NULL);
1340 if (!NT_SUCCESS(Status))
1341 {
1342 return Status;
1343 }
1344
1345 switch (ThreadInformationClass)
1346 {
1347 case ThreadBasicInformation:
1348 /* A test on W2K agains ntdll shows NtQueryInformationThread return STATUS_PENDING
1349 * as ExitStatus for current/running thread, while KETHREAD's ExitStatus is
1350 * 0. So do the conversion here:
1351 * -Gunnar */
1352 u.TBI.ExitStatus = (Thread->ExitStatus == 0) ? STATUS_PENDING : Thread->ExitStatus;
1353 u.TBI.TebBaseAddress = Thread->Tcb.Teb;
1354 u.TBI.ClientId = Thread->Cid;
1355 u.TBI.AffinityMask = Thread->Tcb.Affinity;
1356 u.TBI.Priority = Thread->Tcb.Priority;
1357 u.TBI.BasePriority = Thread->Tcb.BasePriority;
1358 break;
1359
1360 case ThreadTimes:
1361 u.TTI.KernelTime.QuadPart = Thread->Tcb.KernelTime * 100000LL;
1362 u.TTI.UserTime.QuadPart = Thread->Tcb.UserTime * 100000LL;
1363 u.TTI.CreateTime = Thread->CreateTime;
1364 /*This works*/
1365 u.TTI.ExitTime = Thread->ExitTime;
1366 break;
1367
1368 case ThreadQuerySetWin32StartAddress:
1369 u.Address = Thread->Win32StartAddress;
1370 break;
1371
1372 case ThreadPerformanceCount:
1373 /* Nebbett says this class is always zero */
1374 u.Count.QuadPart = 0;
1375 break;
1376
1377 case ThreadAmILastThread:
1378 if (Thread->ThreadsProcess->ThreadListHead.Flink->Flink ==
1379 &Thread->ThreadsProcess->ThreadListHead)
1380 {
1381 u.Last = TRUE;
1382 }
1383 else
1384 {
1385 u.Last = FALSE;
1386 }
1387 break;
1388 default:
1389 /* Shoult never occure if the data table is correct */
1390 KEBUGCHECK(0);
1391 }
1392 if (QueryInformationData[ThreadInformationClass].Size)
1393 {
1394 Status = MmCopyToCaller(ThreadInformation,
1395 &u.TBI,
1396 QueryInformationData[ThreadInformationClass].Size);
1397 }
1398 if (ReturnLength)
1399 {
1400 NTSTATUS Status2;
1401 static ULONG Null = 0;
1402 Status2 = MmCopyToCaller(ReturnLength,
1403 NT_SUCCESS(Status) ? &QueryInformationData[ThreadInformationClass].Size : &Null,
1404 sizeof(ULONG));
1405 if (NT_SUCCESS(Status))
1406 {
1407 Status = Status2;
1408 }
1409 }
1410
1411 ObDereferenceObject(Thread);
1412 return(Status);
1413 }
1414 /* EOF */