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  */
7 
8 #include "precomp.h"
9 
10 static LARGE_INTEGER TestStartTime;
11 
12 static
13 void
14 Test_ProcessTimes(void)
15 {
16 #define SPIN_TIME 1000000
17     NTSTATUS Status;
18     KERNEL_USER_TIMES Times1;
19     KERNEL_USER_TIMES Times2;
20     ULONG Length;
21     LARGE_INTEGER Time1, Time2;
22 
23     /* Everything is NULL */
24     Status = NtQueryInformationProcess(NULL,
25                                        ProcessTimes,
26                                        NULL,
27                                        0,
28                                        NULL);
29     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
30 
31     /* Right size, invalid process */
32     Status = NtQueryInformationProcess(NULL,
33                                        ProcessTimes,
34                                        NULL,
35                                        sizeof(KERNEL_USER_TIMES),
36                                        NULL);
37     ok_hex(Status, STATUS_INVALID_HANDLE);
38 
39     /* Valid process, no buffer */
40     Status = NtQueryInformationProcess(NtCurrentProcess(),
41                                        ProcessTimes,
42                                        NULL,
43                                        0,
44                                        NULL);
45     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
46 
47     /* Unaligned buffer, wrong size */
48     Status = NtQueryInformationProcess(NtCurrentProcess(),
49                                        ProcessTimes,
50                                        (PVOID)2,
51                                        0,
52                                        NULL);
53     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
54 
55     /* Unaligned buffer, correct size */
56     Status = NtQueryInformationProcess(NtCurrentProcess(),
57                                        ProcessTimes,
58                                        (PVOID)2,
59                                        sizeof(KERNEL_USER_TIMES),
60                                        NULL);
61     ok_hex(Status, STATUS_DATATYPE_MISALIGNMENT);
62 
63     /* Buffer too small */
64     Status = NtQueryInformationProcess(NtCurrentProcess(),
65                                        ProcessTimes,
66                                        NULL,
67                                        sizeof(KERNEL_USER_TIMES) - 1,
68                                        NULL);
69     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
70 
71     /* Right buffer size but NULL pointer */
72     Status = NtQueryInformationProcess(NtCurrentProcess(),
73                                        ProcessTimes,
74                                        NULL,
75                                        sizeof(KERNEL_USER_TIMES),
76                                        NULL);
77     ok_hex(Status, STATUS_ACCESS_VIOLATION);
78 
79     /* Buffer too large */
80     Status = NtQueryInformationProcess(NtCurrentProcess(),
81                                        ProcessTimes,
82                                        NULL,
83                                        sizeof(KERNEL_USER_TIMES) + 1,
84                                        NULL);
85     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
86 
87     /* Buffer too small, ask for length */
88     Length = 0x55555555;
89     Status = NtQueryInformationProcess(NtCurrentProcess(),
90                                        ProcessTimes,
91                                        NULL,
92                                        sizeof(KERNEL_USER_TIMES) - 1,
93                                        &Length);
94     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
95     ok_dec(Length, 0x55555555);
96 
97     Status = NtQuerySystemTime(&Time1);
98     ok_hex(Status, STATUS_SUCCESS);
99 
100     /* Do some busy waiting to increase UserTime */
101     do
102     {
103         Status = NtQuerySystemTime(&Time2);
104         if (!NT_SUCCESS(Status))
105         {
106             ok(0, "NtQuerySystemTime failed with %lx\n", Status);
107             break;
108         }
109     } while (Time2.QuadPart - Time1.QuadPart < SPIN_TIME);
110 
111     /* Valid parameters, no return length */
112     Status = NtQuerySystemTime(&Time1);
113     ok_hex(Status, STATUS_SUCCESS);
114 
115     RtlFillMemory(&Times1, sizeof(Times1), 0x55);
116     Status = NtQueryInformationProcess(NtCurrentProcess(),
117                                        ProcessTimes,
118                                        &Times1,
119                                        sizeof(KERNEL_USER_TIMES),
120                                        NULL);
121     ok_hex(Status, STATUS_SUCCESS);
122     ros_skip_flaky
123     ok(Times1.CreateTime.QuadPart < TestStartTime.QuadPart,
124        "CreateTime is %I64u, expected < %I64u\n", Times1.CreateTime.QuadPart, TestStartTime.QuadPart);
125     ok(Times1.CreateTime.QuadPart > TestStartTime.QuadPart - 100000000LL,
126        "CreateTime is %I64u, expected > %I64u\n", Times1.CreateTime.QuadPart, TestStartTime.QuadPart - 100000000LL);
127     ok(Times1.ExitTime.QuadPart == 0,
128        "ExitTime is %I64u, expected 0\n", Times1.ExitTime.QuadPart);
129     ok(Times1.KernelTime.QuadPart != 0, "KernelTime is 0\n");
130     ros_skip_flaky
131     ok(Times1.UserTime.QuadPart != 0, "UserTime is 0\n");
132 
133     /* Do some busy waiting to increase UserTime */
134     do
135     {
136         Status = NtQuerySystemTime(&Time2);
137         if (!NT_SUCCESS(Status))
138         {
139             ok(0, "NtQuerySystemTime failed with %lx\n", Status);
140             break;
141         }
142     } while (Time2.QuadPart - Time1.QuadPart < SPIN_TIME);
143 
144     /* Again, this time with a return length */
145     Length = 0x55555555;
146     RtlFillMemory(&Times2, sizeof(Times2), 0x55);
147     Status = NtQueryInformationProcess(NtCurrentProcess(),
148                                        ProcessTimes,
149                                        &Times2,
150                                        sizeof(KERNEL_USER_TIMES),
151                                        &Length);
152     ok_hex(Status, STATUS_SUCCESS);
153     ok_dec(Length, sizeof(KERNEL_USER_TIMES));
154     ok(Times1.CreateTime.QuadPart == Times2.CreateTime.QuadPart,
155        "CreateTimes not equal: %I64u != %I64u\n", Times1.CreateTime.QuadPart, Times2.CreateTime.QuadPart);
156     ok(Times2.ExitTime.QuadPart == 0,
157        "ExitTime is %I64u, expected 0\n", Times2.ExitTime.QuadPart);
158     ok(Times2.KernelTime.QuadPart != 0, "KernelTime is 0\n");
159     ok(Times2.UserTime.QuadPart != 0, "UserTime is 0\n");
160 
161     /* Compare the two sets of KernelTime/UserTime values */
162     Status = NtQuerySystemTime(&Time2);
163     ok_hex(Status, STATUS_SUCCESS);
164     /* Time values must have increased */
165     ok(Times2.KernelTime.QuadPart > Times1.KernelTime.QuadPart,
166        "KernelTime values inconsistent. Expected %I64u > %I64u\n", Times2.KernelTime.QuadPart, Times1.KernelTime.QuadPart);
167     ros_skip_flaky
168     ok(Times2.UserTime.QuadPart > Times1.UserTime.QuadPart,
169        "UserTime values inconsistent. Expected %I64u > %I64u\n", Times2.UserTime.QuadPart, Times1.UserTime.QuadPart);
170     /* They can't have increased by more than wall clock time difference (we only have one thread) */
171     ros_skip_flaky
172     ok(Times2.KernelTime.QuadPart - Times1.KernelTime.QuadPart < Time2.QuadPart - Time1.QuadPart,
173        "KernelTime values inconsistent. Expected %I64u - %I64u < %I64u\n",
174        Times2.KernelTime.QuadPart, Times1.KernelTime.QuadPart, Time2.QuadPart - Time1.QuadPart);
175     ok(Times2.UserTime.QuadPart - Times1.UserTime.QuadPart < Time2.QuadPart - Time1.QuadPart,
176        "UserTime values inconsistent. Expected %I64u - %I64u < %I64u\n",
177        Times2.UserTime.QuadPart, Times1.UserTime.QuadPart, Time2.QuadPart - Time1.QuadPart);
178 
179     trace("KernelTime1 = %I64u\n", Times1.KernelTime.QuadPart);
180     trace("KernelTime2 = %I64u\n", Times2.KernelTime.QuadPart);
181     trace("UserTime1 = %I64u\n", Times1.UserTime.QuadPart);
182     trace("UserTime2 = %I64u\n", Times2.UserTime.QuadPart);
183 
184     /* TODO: Test ExitTime on a terminated process */
185 #undef SPIN_TIME
186 }
187 
188 static
189 void
190 Test_ProcessPriorityClassAlignment(void)
191 {
192     NTSTATUS Status;
193     PPROCESS_PRIORITY_CLASS ProcPriority;
194 
195     /* Allocate some memory for the priority class structure */
196     ProcPriority = malloc(sizeof(PROCESS_PRIORITY_CLASS));
197     if (ProcPriority == NULL)
198     {
199         skip("Failed to allocate memory for PROCESS_PRIORITY_CLASS!\n");
200         return;
201     }
202 
203     /*
204      * Initialize the PriorityClass member to ensure the test won't randomly succeed (if such data is uninitialized).
205      * Filling 85 to the data member makes sure that if the test fails continously then NtQueryInformationProcess()
206      * didn't initialize the structure with data.
207      */
208     RtlFillMemory(&ProcPriority->PriorityClass, sizeof(ProcPriority->PriorityClass), 0x55);
209 
210     /* Unaligned buffer -- wrong size */
211     Status = NtQueryInformationProcess(NtCurrentProcess(),
212                                        ProcessPriorityClass,
213                                        (PVOID)1,
214                                        0,
215                                        NULL);
216     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
217 
218     /* Unaligned buffer -- correct size */
219     Status = NtQueryInformationProcess(NtCurrentProcess(),
220                                        ProcessPriorityClass,
221                                        (PVOID)1,
222                                        sizeof(PROCESS_PRIORITY_CLASS),
223                                        NULL);
224     ok_hex(Status, STATUS_DATATYPE_MISALIGNMENT);
225 
226     /* Unaligned buffer -- wrong size (but this time do with an alignment of 2) */
227     Status = NtQueryInformationProcess(NtCurrentProcess(),
228                                        ProcessPriorityClass,
229                                        (PVOID)2,
230                                        0,
231                                        NULL);
232     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
233 
234     /* Unaligned buffer -- correct size (but this time do with an alignment of 2) */
235     Status = NtQueryInformationProcess(NtCurrentProcess(),
236                                        ProcessPriorityClass,
237                                        (PVOID)2,
238                                        sizeof(PROCESS_PRIORITY_CLASS),
239                                        NULL);
240     ok_hex(Status, STATUS_DATATYPE_MISALIGNMENT);
241 
242     /* Do not care for the length but expect to return the priority class */
243     Status = NtQueryInformationProcess(NtCurrentProcess(),
244                                        ProcessPriorityClass,
245                                        ProcPriority,
246                                        sizeof(PROCESS_PRIORITY_CLASS),
247                                        NULL);
248     ok_hex(Status, STATUS_SUCCESS);
249 
250     /* Make sure the returned priority class is a valid number (non negative) but also it should be within the PROCESS_PRIORITY_CLASS range */
251     ok(ProcPriority->PriorityClass > PROCESS_PRIORITY_CLASS_INVALID && ProcPriority->PriorityClass <= PROCESS_PRIORITY_CLASS_ABOVE_NORMAL,
252        "Expected a valid number from priority class range but got %d\n", ProcPriority->PriorityClass);
253     free(ProcPriority);
254 }
255 
256 static
257 void
258 Test_ProcessWx86Information(void)
259 {
260     NTSTATUS Status;
261     ULONG VdmPower = 1, ReturnLength;
262 
263     /* Everything is NULL */
264     Status = NtQueryInformationProcess(NULL,
265                                        ProcessWx86Information,
266                                        NULL,
267                                        0,
268                                        NULL);
269     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
270 
271     /* Given an invalid process handle */
272     Status = NtQueryInformationProcess(NULL,
273                                        ProcessWx86Information,
274                                        &VdmPower,
275                                        sizeof(VdmPower),
276                                        NULL);
277     ok_hex(Status, STATUS_INVALID_HANDLE);
278 
279     /* Don't query anything */
280     Status = NtQueryInformationProcess(NtCurrentProcess(),
281                                        ProcessWx86Information,
282                                        NULL,
283                                        sizeof(VdmPower),
284                                        NULL);
285     ok_hex(Status, STATUS_ACCESS_VIOLATION);
286 
287     /* The buffer is misaligned and information length is wrong */
288     Status = NtQueryInformationProcess(NtCurrentProcess(),
289                                        ProcessWx86Information,
290                                        (PVOID)1,
291                                        0,
292                                        NULL);
293     ok_hex(Status, STATUS_INFO_LENGTH_MISMATCH);
294 
295     /* The buffer is misaligned */
296     Status = NtQueryInformationProcess(NtCurrentProcess(),
297                                        ProcessWx86Information,
298                                        (PVOID)1,
299                                        sizeof(VdmPower),
300                                        NULL);
301     ok_hex(Status, STATUS_DATATYPE_MISALIGNMENT);
302 
303     /* The buffer is misaligned -- try with an alignment size of 2 */
304     Status = NtQueryInformationProcess(NtCurrentProcess(),
305                                        ProcessWx86Information,
306                                        (PVOID)2,
307                                        sizeof(VdmPower),
308                                        NULL);
309     ok_hex(Status, STATUS_DATATYPE_MISALIGNMENT);
310 
311     /* Query the VDM power */
312     Status = NtQueryInformationProcess(NtCurrentProcess(),
313                                        ProcessWx86Information,
314                                        &VdmPower,
315                                        sizeof(VdmPower),
316                                        &ReturnLength);
317     ok_hex(Status, STATUS_SUCCESS);
318     ok(ReturnLength != 0, "ReturnLength shouldn't be 0!\n");
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     /* Trace the VDM power value and returned length */
322     trace("ReturnLength = %lu\n", ReturnLength);
323     trace("VdmPower = %lu\n", VdmPower);
324 }
325 
326 START_TEST(NtQueryInformationProcess)
327 {
328     NTSTATUS Status;
329 
330     Status = NtQuerySystemTime(&TestStartTime);
331     ok_hex(Status, STATUS_SUCCESS);
332 
333     Test_ProcessTimes();
334     Test_ProcessPriorityClassAlignment();
335     Test_ProcessWx86Information();
336 }
337