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_ProcessPriorityClassAlignment(void)
192 {
193     NTSTATUS Status;
194     PPROCESS_PRIORITY_CLASS ProcPriority;
195 
196     /* Allocate some memory for the priority class structure */
197     ProcPriority = malloc(sizeof(PROCESS_PRIORITY_CLASS));
198     if (ProcPriority == NULL)
199     {
200         skip("Failed to allocate memory for PROCESS_PRIORITY_CLASS!\n");
201         return;
202     }
203 
204     /*
205      * Initialize the PriorityClass member to ensure the test won't randomly succeed (if such data is uninitialized).
206      * Filling 85 to the data member makes sure that if the test fails continously then NtQueryInformationProcess()
207      * didn't initialize the structure with data.
208      */
209     RtlFillMemory(&ProcPriority->PriorityClass, sizeof(ProcPriority->PriorityClass), 0x55);
210 
211     /* Unaligned buffer -- wrong size */
212     Status = NtQueryInformationProcess(NtCurrentProcess(),
213                                        ProcessPriorityClass,
214                                        (PVOID)1,
215                                        0,
216                                        NULL);
217     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
218 
219     /* Unaligned buffer -- correct size */
220     Status = NtQueryInformationProcess(NtCurrentProcess(),
221                                        ProcessPriorityClass,
222                                        (PVOID)1,
223                                        sizeof(PROCESS_PRIORITY_CLASS),
224                                        NULL);
225     ok_hex(Status, STATUS_DATATYPE_MISALIGNMENT);
226 
227     /* Unaligned buffer -- wrong size (but this time do with an alignment of 2) */
228     Status = NtQueryInformationProcess(NtCurrentProcess(),
229                                        ProcessPriorityClass,
230                                        (PVOID)2,
231                                        0,
232                                        NULL);
233     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
234 
235     /* Unaligned buffer -- correct size (but this time do with an alignment of 2) */
236     Status = NtQueryInformationProcess(NtCurrentProcess(),
237                                        ProcessPriorityClass,
238                                        (PVOID)2,
239                                        sizeof(PROCESS_PRIORITY_CLASS),
240                                        NULL);
241     ok_hex(Status, STATUS_DATATYPE_MISALIGNMENT);
242 
243     /* Do not care for the length but expect to return the priority class */
244     Status = NtQueryInformationProcess(NtCurrentProcess(),
245                                        ProcessPriorityClass,
246                                        ProcPriority,
247                                        sizeof(PROCESS_PRIORITY_CLASS),
248                                        NULL);
249     ok_hex(Status, STATUS_SUCCESS);
250 
251     /* Make sure the returned priority class is a valid number (non negative) but also it should be within the PROCESS_PRIORITY_CLASS range */
252     ok(ProcPriority->PriorityClass > PROCESS_PRIORITY_CLASS_INVALID && ProcPriority->PriorityClass <= PROCESS_PRIORITY_CLASS_ABOVE_NORMAL,
253        "Expected a valid number from priority class range but got %d\n", ProcPriority->PriorityClass);
254     free(ProcPriority);
255 }
256 
257 static
258 void
259 Test_ProcessWx86Information(void)
260 {
261     NTSTATUS Status;
262     ULONG VdmPower = 1, ReturnLength;
263 
264     /* Everything is NULL */
265     Status = NtQueryInformationProcess(NULL,
266                                        ProcessWx86Information,
267                                        NULL,
268                                        0,
269                                        NULL);
270     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
271 
272     /* Given an invalid process handle */
273     Status = NtQueryInformationProcess(NULL,
274                                        ProcessWx86Information,
275                                        &VdmPower,
276                                        sizeof(VdmPower),
277                                        NULL);
278     ok_hex(Status, STATUS_INVALID_HANDLE);
279 
280     /* Don't query anything */
281     Status = NtQueryInformationProcess(NtCurrentProcess(),
282                                        ProcessWx86Information,
283                                        NULL,
284                                        sizeof(VdmPower),
285                                        NULL);
286     ok_hex(Status, STATUS_ACCESS_VIOLATION);
287 
288     /* The buffer is misaligned and information length is wrong */
289     Status = NtQueryInformationProcess(NtCurrentProcess(),
290                                        ProcessWx86Information,
291                                        (PVOID)1,
292                                        0,
293                                        NULL);
294     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
295 
296     /* The buffer is misaligned */
297     Status = NtQueryInformationProcess(NtCurrentProcess(),
298                                        ProcessWx86Information,
299                                        (PVOID)1,
300                                        sizeof(VdmPower),
301                                        NULL);
302     ok_hex(Status, STATUS_DATATYPE_MISALIGNMENT);
303 
304     /* The buffer is misaligned -- try with an alignment size of 2 */
305     Status = NtQueryInformationProcess(NtCurrentProcess(),
306                                        ProcessWx86Information,
307                                        (PVOID)2,
308                                        sizeof(VdmPower),
309                                        NULL);
310     ok_hex(Status, STATUS_DATATYPE_MISALIGNMENT);
311 
312     /* Query the VDM power */
313     Status = NtQueryInformationProcess(NtCurrentProcess(),
314                                        ProcessWx86Information,
315                                        &VdmPower,
316                                        sizeof(VdmPower),
317                                        NULL);
318     ok_hex(Status, STATUS_SUCCESS);
319     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);
320 
321     /* Same but with ReturnLength */
322     Status = NtQueryInformationProcess(NtCurrentProcess(),
323                                        ProcessWx86Information,
324                                        &VdmPower,
325                                        sizeof(VdmPower),
326                                        &ReturnLength);
327     ok_hex(Status, STATUS_SUCCESS);
328     ok(ReturnLength != 0, "ReturnLength shouldn't be 0!\n");
329     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);
330 
331     /* Trace the VDM power value and returned length */
332     trace("ReturnLength = %lu\n", ReturnLength);
333     trace("VdmPower = %lu\n", VdmPower);
334 }
335 
336 static
337 void
338 Test_ProcQueryAlignmentProbe(void)
339 {
340     ULONG InfoClass;
341 
342     /* Iterate over the process info classes and begin the tests */
343     for (InfoClass = 0; InfoClass < _countof(PsProcessInfoClass); InfoClass++)
344     {
345         /* The buffer is misaligned */
346         QuerySetProcessValidator(QUERY,
347                                  InfoClass,
348                                  (PVOID)(ULONG_PTR)1,
349                                  PsProcessInfoClass[InfoClass].RequiredSizeQUERY,
350                                  STATUS_DATATYPE_MISALIGNMENT);
351 
352         /* We query an invalid buffer address */
353         QuerySetProcessValidator(QUERY,
354                                  InfoClass,
355                                  (PVOID)(ULONG_PTR)PsProcessInfoClass[InfoClass].AlignmentQUERY,
356                                  PsProcessInfoClass[InfoClass].RequiredSizeQUERY,
357                                  STATUS_ACCESS_VIOLATION);
358 
359         /* The information length is wrong */
360         QuerySetProcessValidator(QUERY,
361                                  InfoClass,
362                                  (PVOID)(ULONG_PTR)PsProcessInfoClass[InfoClass].AlignmentQUERY,
363                                  PsProcessInfoClass[InfoClass].RequiredSizeQUERY - 1,
364                                  STATUS_INFO_LENGTH_MISMATCH);
365     }
366 }
367 
368 START_TEST(NtQueryInformationProcess)
369 {
370     NTSTATUS Status;
371 
372     /* Make sure that some time has passed since process creation, even if the resolution of our NtQuerySystemTime is low. */
373     Sleep(1);
374 
375     Status = NtQuerySystemTime(&TestStartTime);
376     ok_hex(Status, STATUS_SUCCESS);
377 
378     Test_ProcessTimes();
379     Test_ProcessPriorityClassAlignment();
380     Test_ProcessWx86Information();
381     Test_ProcQueryAlignmentProbe();
382 }
383