1 /*
2  * PROJECT:         ReactOS API tests
3  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
4  * PURPOSE:         Tests for the NtQueryInformationProcess API
5  * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
6  *                  George Bișoc <george.bisoc@reactos.org>
7  */
8 
9 #include "precomp.h"
10 #include <internal/ps_i.h>
11 
12 static LARGE_INTEGER TestStartTime;
13 
14 static
15 void
16 Test_ProcessTimes(void)
17 {
18 #define SPIN_TIME 1000000
19     NTSTATUS Status;
20     KERNEL_USER_TIMES Times1;
21     KERNEL_USER_TIMES Times2;
22     ULONG Length;
23     LARGE_INTEGER Time1, Time2;
24 
25     /* Everything is NULL */
26     Status = NtQueryInformationProcess(NULL,
27                                        ProcessTimes,
28                                        NULL,
29                                        0,
30                                        NULL);
31     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
32 
33     /* Right size, invalid process */
34     Status = NtQueryInformationProcess(NULL,
35                                        ProcessTimes,
36                                        NULL,
37                                        sizeof(KERNEL_USER_TIMES),
38                                        NULL);
39     ok_hex(Status, STATUS_INVALID_HANDLE);
40 
41     /* Valid process, no buffer */
42     Status = NtQueryInformationProcess(NtCurrentProcess(),
43                                        ProcessTimes,
44                                        NULL,
45                                        0,
46                                        NULL);
47     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
48 
49     /* Unaligned buffer, wrong size */
50     Status = NtQueryInformationProcess(NtCurrentProcess(),
51                                        ProcessTimes,
52                                        (PVOID)2,
53                                        0,
54                                        NULL);
55     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
56 
57     /* Unaligned buffer, correct size */
58     Status = NtQueryInformationProcess(NtCurrentProcess(),
59                                        ProcessTimes,
60                                        (PVOID)2,
61                                        sizeof(KERNEL_USER_TIMES),
62                                        NULL);
63     ok_hex(Status, STATUS_DATATYPE_MISALIGNMENT);
64 
65     /* Buffer too small */
66     Status = NtQueryInformationProcess(NtCurrentProcess(),
67                                        ProcessTimes,
68                                        NULL,
69                                        sizeof(KERNEL_USER_TIMES) - 1,
70                                        NULL);
71     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
72 
73     /* Right buffer size but NULL pointer */
74     Status = NtQueryInformationProcess(NtCurrentProcess(),
75                                        ProcessTimes,
76                                        NULL,
77                                        sizeof(KERNEL_USER_TIMES),
78                                        NULL);
79     ok_hex(Status, STATUS_ACCESS_VIOLATION);
80 
81     /* Buffer too large */
82     Status = NtQueryInformationProcess(NtCurrentProcess(),
83                                        ProcessTimes,
84                                        NULL,
85                                        sizeof(KERNEL_USER_TIMES) + 1,
86                                        NULL);
87     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
88 
89     /* Buffer too small, ask for length */
90     Length = 0x55555555;
91     Status = NtQueryInformationProcess(NtCurrentProcess(),
92                                        ProcessTimes,
93                                        NULL,
94                                        sizeof(KERNEL_USER_TIMES) - 1,
95                                        &Length);
96     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
97     ok_dec(Length, 0x55555555);
98 
99     Status = NtQuerySystemTime(&Time1);
100     ok_hex(Status, STATUS_SUCCESS);
101 
102     /* Do some busy waiting to increase UserTime */
103     do
104     {
105         Status = NtQuerySystemTime(&Time2);
106         if (!NT_SUCCESS(Status))
107         {
108             ok(0, "NtQuerySystemTime failed with %lx\n", Status);
109             break;
110         }
111     } while (Time2.QuadPart - Time1.QuadPart < SPIN_TIME);
112 
113     /* Valid parameters, no return length */
114     Status = NtQuerySystemTime(&Time1);
115     ok_hex(Status, STATUS_SUCCESS);
116 
117     RtlFillMemory(&Times1, sizeof(Times1), 0x55);
118     Status = NtQueryInformationProcess(NtCurrentProcess(),
119                                        ProcessTimes,
120                                        &Times1,
121                                        sizeof(KERNEL_USER_TIMES),
122                                        NULL);
123     ok_hex(Status, STATUS_SUCCESS);
124     ok(Times1.CreateTime.QuadPart < TestStartTime.QuadPart,
125        "CreateTime is %I64u, expected < %I64u\n", Times1.CreateTime.QuadPart, TestStartTime.QuadPart);
126     ok(Times1.CreateTime.QuadPart > TestStartTime.QuadPart - 100000000LL,
127        "CreateTime is %I64u, expected > %I64u\n", Times1.CreateTime.QuadPart, TestStartTime.QuadPart - 100000000LL);
128     ok(Times1.ExitTime.QuadPart == 0,
129        "ExitTime is %I64u, expected 0\n", Times1.ExitTime.QuadPart);
130     ok(Times1.KernelTime.QuadPart != 0, "KernelTime is 0\n");
131     ros_skip_flaky
132     ok(Times1.UserTime.QuadPart != 0, "UserTime is 0\n");
133 
134     /* Do some busy waiting to increase UserTime */
135     do
136     {
137         Status = NtQuerySystemTime(&Time2);
138         if (!NT_SUCCESS(Status))
139         {
140             ok(0, "NtQuerySystemTime failed with %lx\n", Status);
141             break;
142         }
143     } while (Time2.QuadPart - Time1.QuadPart < SPIN_TIME);
144 
145     /* Again, this time with a return length */
146     Length = 0x55555555;
147     RtlFillMemory(&Times2, sizeof(Times2), 0x55);
148     Status = NtQueryInformationProcess(NtCurrentProcess(),
149                                        ProcessTimes,
150                                        &Times2,
151                                        sizeof(KERNEL_USER_TIMES),
152                                        &Length);
153     ok_hex(Status, STATUS_SUCCESS);
154     ok_dec(Length, sizeof(KERNEL_USER_TIMES));
155     ok(Times1.CreateTime.QuadPart == Times2.CreateTime.QuadPart,
156        "CreateTimes not equal: %I64u != %I64u\n", Times1.CreateTime.QuadPart, Times2.CreateTime.QuadPart);
157     ok(Times2.ExitTime.QuadPart == 0,
158        "ExitTime is %I64u, expected 0\n", Times2.ExitTime.QuadPart);
159     ok(Times2.KernelTime.QuadPart != 0, "KernelTime is 0\n");
160     ok(Times2.UserTime.QuadPart != 0, "UserTime is 0\n");
161 
162     /* Compare the two sets of KernelTime/UserTime values */
163     Status = NtQuerySystemTime(&Time2);
164     ok_hex(Status, STATUS_SUCCESS);
165     /* Time values must have increased */
166     ok(Times2.KernelTime.QuadPart > Times1.KernelTime.QuadPart,
167        "KernelTime values inconsistent. Expected %I64u > %I64u\n", Times2.KernelTime.QuadPart, Times1.KernelTime.QuadPart);
168     ros_skip_flaky
169     ok(Times2.UserTime.QuadPart > Times1.UserTime.QuadPart,
170        "UserTime values inconsistent. Expected %I64u > %I64u\n", Times2.UserTime.QuadPart, Times1.UserTime.QuadPart);
171     /* They can't have increased by more than wall clock time difference (we only have one thread) */
172     ros_skip_flaky
173     ok(Times2.KernelTime.QuadPart - Times1.KernelTime.QuadPart < Time2.QuadPart - Time1.QuadPart,
174        "KernelTime values inconsistent. Expected %I64u - %I64u < %I64u\n",
175        Times2.KernelTime.QuadPart, Times1.KernelTime.QuadPart, Time2.QuadPart - Time1.QuadPart);
176     ok(Times2.UserTime.QuadPart - Times1.UserTime.QuadPart < Time2.QuadPart - Time1.QuadPart,
177        "UserTime values inconsistent. Expected %I64u - %I64u < %I64u\n",
178        Times2.UserTime.QuadPart, Times1.UserTime.QuadPart, Time2.QuadPart - Time1.QuadPart);
179 
180     trace("KernelTime1 = %I64u\n", Times1.KernelTime.QuadPart);
181     trace("KernelTime2 = %I64u\n", Times2.KernelTime.QuadPart);
182     trace("UserTime1 = %I64u\n", Times1.UserTime.QuadPart);
183     trace("UserTime2 = %I64u\n", Times2.UserTime.QuadPart);
184 
185     /* TODO: Test ExitTime on a terminated process */
186 #undef SPIN_TIME
187 }
188 
189 static
190 void
191 Test_ProcessBasicInformation(void)
192 {
193     NTSTATUS Status;
194     ULONG Length;
195     PROCESS_BASIC_INFORMATION BasicInfo;
196 
197     /* Everything is NULL */
198     Status = NtQueryInformationProcess(NULL,
199                                        ProcessBasicInformation,
200                                        NULL,
201                                        0,
202                                        NULL);
203     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
204 
205     /* Right size, invalid process handle */
206     Status = NtQueryInformationProcess(NULL,
207                                        ProcessBasicInformation,
208                                        NULL,
209                                        sizeof(BasicInfo),
210                                        NULL);
211     ok_hex(Status, STATUS_INVALID_HANDLE);
212 
213     /* Valid process handle, no buffer */
214     Status = NtQueryInformationProcess(NtCurrentProcess(),
215                                        ProcessBasicInformation,
216                                        NULL,
217                                        0,
218                                        NULL);
219     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
220 
221     /* Unaligned buffer, wrong size */
222     Status = NtQueryInformationProcess(NtCurrentProcess(),
223                                        ProcessBasicInformation,
224                                        (PVOID)2,
225                                        0,
226                                        NULL);
227     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
228 
229     /* Unaligned buffer, correct size */
230     Status = NtQueryInformationProcess(NtCurrentProcess(),
231                                        ProcessBasicInformation,
232                                        (PVOID)2,
233                                        sizeof(BasicInfo),
234                                        NULL);
235     ok_hex(Status, STATUS_DATATYPE_MISALIGNMENT);
236 
237     /* Buffer too small */
238     Status = NtQueryInformationProcess(NtCurrentProcess(),
239                                        ProcessBasicInformation,
240                                        NULL,
241                                        sizeof(BasicInfo) - 1,
242                                        NULL);
243     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
244 
245     /* Right buffer size but NULL pointer */
246     Status = NtQueryInformationProcess(NtCurrentProcess(),
247                                        ProcessBasicInformation,
248                                        NULL,
249                                        sizeof(BasicInfo),
250                                        NULL);
251     ok_hex(Status, STATUS_ACCESS_VIOLATION);
252 
253     /* Buffer too large */
254     Status = NtQueryInformationProcess(NtCurrentProcess(),
255                                        ProcessBasicInformation,
256                                        NULL,
257                                        sizeof(BasicInfo) + 1,
258                                        NULL);
259     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
260 
261     /* Buffer too small, ask for length */
262     Length = 0x55555555;
263     Status = NtQueryInformationProcess(NtCurrentProcess(),
264                                        ProcessBasicInformation,
265                                        NULL,
266                                        sizeof(BasicInfo) - 1,
267                                        &Length);
268     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
269     ok_dec(Length, 0x55555555);
270 
271     /* Valid parameters, no return length */
272     RtlFillMemory(&BasicInfo, sizeof(BasicInfo), 0x55);
273     Status = NtQueryInformationProcess(NtCurrentProcess(),
274                                        ProcessBasicInformation,
275                                        &BasicInfo,
276                                        sizeof(BasicInfo),
277                                        NULL);
278     ok_hex(Status, STATUS_SUCCESS);
279 
280     /* Trace the returned data (1) */
281     trace("[1] BasicInfo.ExitStatus = %lx\n", BasicInfo.ExitStatus);
282     trace("[1] BasicInfo.PebBaseAddress = %p\n", BasicInfo.PebBaseAddress);
283     trace("[1] BasicInfo.AffinityMask = %Ix\n", BasicInfo.AffinityMask);
284     trace("[1] BasicInfo.BasePriority = %ld\n", BasicInfo.BasePriority);
285     trace("[1] BasicInfo.UniqueProcessId = %Iu\n", BasicInfo.UniqueProcessId);
286     trace("[1] BasicInfo.InheritedFromUniqueProcessId = %Iu\n", BasicInfo.InheritedFromUniqueProcessId);
287 
288     /* Again, this time with a return length */
289     Length = 0x55555555;
290     RtlFillMemory(&BasicInfo, sizeof(BasicInfo), 0x55);
291     Status = NtQueryInformationProcess(NtCurrentProcess(),
292                                        ProcessBasicInformation,
293                                        &BasicInfo,
294                                        sizeof(BasicInfo),
295                                        &Length);
296     ok_hex(Status, STATUS_SUCCESS);
297     ok_dec(Length, sizeof(BasicInfo));
298 
299     /* Trace the returned data (2) */
300     trace("[2] BasicInfo.ExitStatus = %lx\n", BasicInfo.ExitStatus);
301     trace("[2] BasicInfo.PebBaseAddress = %p\n", BasicInfo.PebBaseAddress);
302     trace("[2] BasicInfo.AffinityMask = %Ix\n", BasicInfo.AffinityMask);
303     trace("[2] BasicInfo.BasePriority = %ld\n", BasicInfo.BasePriority);
304     trace("[2] BasicInfo.UniqueProcessId = %Iu\n", BasicInfo.UniqueProcessId);
305     trace("[2] BasicInfo.InheritedFromUniqueProcessId = %Iu\n", BasicInfo.InheritedFromUniqueProcessId);
306 }
307 
308 static
309 void
310 Test_ProcessPriorityClassAlignment(void)
311 {
312     NTSTATUS Status;
313     PPROCESS_PRIORITY_CLASS ProcPriority;
314 
315     /* Allocate some memory for the priority class structure */
316     ProcPriority = malloc(sizeof(PROCESS_PRIORITY_CLASS));
317     if (ProcPriority == NULL)
318     {
319         skip("Failed to allocate memory for PROCESS_PRIORITY_CLASS!\n");
320         return;
321     }
322 
323     /*
324      * Initialize the PriorityClass member to ensure the test won't randomly succeed (if such data is uninitialized).
325      * Filling 85 to the data member makes sure that if the test fails continously then NtQueryInformationProcess()
326      * didn't initialize the structure with data.
327      */
328     RtlFillMemory(&ProcPriority->PriorityClass, sizeof(ProcPriority->PriorityClass), 0x55);
329 
330     /* Unaligned buffer -- wrong size */
331     Status = NtQueryInformationProcess(NtCurrentProcess(),
332                                        ProcessPriorityClass,
333                                        (PVOID)1,
334                                        0,
335                                        NULL);
336     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
337 
338     /* Unaligned buffer -- correct size */
339     Status = NtQueryInformationProcess(NtCurrentProcess(),
340                                        ProcessPriorityClass,
341                                        (PVOID)1,
342                                        sizeof(PROCESS_PRIORITY_CLASS),
343                                        NULL);
344     ok_hex(Status, STATUS_DATATYPE_MISALIGNMENT);
345 
346     /* Unaligned buffer -- wrong size (but this time do with an alignment of 2) */
347     Status = NtQueryInformationProcess(NtCurrentProcess(),
348                                        ProcessPriorityClass,
349                                        (PVOID)2,
350                                        0,
351                                        NULL);
352     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
353 
354     /* Unaligned buffer -- correct size (but this time do with an alignment of 2) */
355     Status = NtQueryInformationProcess(NtCurrentProcess(),
356                                        ProcessPriorityClass,
357                                        (PVOID)2,
358                                        sizeof(PROCESS_PRIORITY_CLASS),
359                                        NULL);
360     ok_hex(Status, STATUS_DATATYPE_MISALIGNMENT);
361 
362     /* Do not care for the length but expect to return the priority class */
363     Status = NtQueryInformationProcess(NtCurrentProcess(),
364                                        ProcessPriorityClass,
365                                        ProcPriority,
366                                        sizeof(PROCESS_PRIORITY_CLASS),
367                                        NULL);
368     ok_hex(Status, STATUS_SUCCESS);
369 
370     /* Make sure the returned priority class is a valid number (non negative) but also it should be within the PROCESS_PRIORITY_CLASS range */
371     ok(ProcPriority->PriorityClass > PROCESS_PRIORITY_CLASS_INVALID && ProcPriority->PriorityClass <= PROCESS_PRIORITY_CLASS_ABOVE_NORMAL,
372        "Expected a valid number from priority class range but got %d\n", ProcPriority->PriorityClass);
373     free(ProcPriority);
374 }
375 
376 static
377 void
378 Test_ProcessWx86Information(void)
379 {
380     NTSTATUS Status;
381     ULONG VdmPower = 1, ReturnLength;
382 
383     /* Everything is NULL */
384     Status = NtQueryInformationProcess(NULL,
385                                        ProcessWx86Information,
386                                        NULL,
387                                        0,
388                                        NULL);
389     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
390 
391     /* Given an invalid process handle */
392     Status = NtQueryInformationProcess(NULL,
393                                        ProcessWx86Information,
394                                        &VdmPower,
395                                        sizeof(VdmPower),
396                                        NULL);
397     ok_hex(Status, STATUS_INVALID_HANDLE);
398 
399     /* Don't query anything */
400     Status = NtQueryInformationProcess(NtCurrentProcess(),
401                                        ProcessWx86Information,
402                                        NULL,
403                                        sizeof(VdmPower),
404                                        NULL);
405     ok_hex(Status, STATUS_ACCESS_VIOLATION);
406 
407     /* The buffer is misaligned and information length is wrong */
408     Status = NtQueryInformationProcess(NtCurrentProcess(),
409                                        ProcessWx86Information,
410                                        (PVOID)1,
411                                        0,
412                                        NULL);
413     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
414 
415     /* The buffer is misaligned */
416     Status = NtQueryInformationProcess(NtCurrentProcess(),
417                                        ProcessWx86Information,
418                                        (PVOID)1,
419                                        sizeof(VdmPower),
420                                        NULL);
421     ok_hex(Status, STATUS_DATATYPE_MISALIGNMENT);
422 
423     /* The buffer is misaligned -- try with an alignment size of 2 */
424     Status = NtQueryInformationProcess(NtCurrentProcess(),
425                                        ProcessWx86Information,
426                                        (PVOID)2,
427                                        sizeof(VdmPower),
428                                        NULL);
429     ok_hex(Status, STATUS_DATATYPE_MISALIGNMENT);
430 
431     /* Query the VDM power */
432     Status = NtQueryInformationProcess(NtCurrentProcess(),
433                                        ProcessWx86Information,
434                                        &VdmPower,
435                                        sizeof(VdmPower),
436                                        NULL);
437     ok_hex(Status, STATUS_SUCCESS);
438     ok(VdmPower == 0 || VdmPower == 1, "The VDM power value must be within the boundary between 0 and 1, not anything else! Got %lu\n", VdmPower);
439 
440     /* Same but with ReturnLength */
441     Status = NtQueryInformationProcess(NtCurrentProcess(),
442                                        ProcessWx86Information,
443                                        &VdmPower,
444                                        sizeof(VdmPower),
445                                        &ReturnLength);
446     ok_hex(Status, STATUS_SUCCESS);
447     ok(ReturnLength != 0, "ReturnLength shouldn't be 0!\n");
448     ok(VdmPower == 0 || VdmPower == 1, "The VDM power value must be within the boundary between 0 and 1, not anything else! Got %lu\n", VdmPower);
449 
450     /* Trace the VDM power value and returned length */
451     trace("ReturnLength = %lu\n", ReturnLength);
452     trace("VdmPower = %lu\n", VdmPower);
453 }
454 
455 static
456 void
457 Test_ProcQueryAlignmentProbe(void)
458 {
459     ULONG InfoClass;
460 
461     /* Iterate over the process info classes and begin the tests */
462     for (InfoClass = 0; InfoClass < _countof(PsProcessInfoClass); InfoClass++)
463     {
464         /* The buffer is misaligned */
465         QuerySetProcessValidator(QUERY,
466                                  InfoClass,
467                                  (PVOID)(ULONG_PTR)1,
468                                  PsProcessInfoClass[InfoClass].RequiredSizeQUERY,
469                                  STATUS_DATATYPE_MISALIGNMENT);
470 
471         /* We query an invalid buffer address */
472         QuerySetProcessValidator(QUERY,
473                                  InfoClass,
474                                  (PVOID)(ULONG_PTR)PsProcessInfoClass[InfoClass].AlignmentQUERY,
475                                  PsProcessInfoClass[InfoClass].RequiredSizeQUERY,
476                                  STATUS_ACCESS_VIOLATION);
477 
478         /* The information length is wrong */
479         QuerySetProcessValidator(QUERY,
480                                  InfoClass,
481                                  (PVOID)(ULONG_PTR)PsProcessInfoClass[InfoClass].AlignmentQUERY,
482                                  PsProcessInfoClass[InfoClass].RequiredSizeQUERY - 1,
483                                  STATUS_INFO_LENGTH_MISMATCH);
484     }
485 }
486 
487 START_TEST(NtQueryInformationProcess)
488 {
489     NTSTATUS Status;
490 
491     /* Make sure that some time has passed since process creation, even if the resolution of our NtQuerySystemTime is low. */
492     Sleep(1);
493 
494     Status = NtQuerySystemTime(&TestStartTime);
495     ok_hex(Status, STATUS_SUCCESS);
496 
497     Test_ProcessTimes();
498     Test_ProcessBasicInformation();
499     Test_ProcessPriorityClassAlignment();
500     Test_ProcessWx86Information();
501     Test_ProcQueryAlignmentProbe();
502 }
503