[KMTESTS]
[reactos.git] / kmtests / ntos_ob / ObType.c
1 /*
2 * PROJECT: ReactOS kernel-mode tests
3 * LICENSE: LGPLv2+ - See COPYING.LIB in the top level directory
4 * PURPOSE: Kernel-Mode Test Suite Ob Regressions KM-Test
5 * PROGRAMMER: Aleksey Bragin <aleksey@reactos.org>
6 * Thomas Faber <thfabba@gmx.de>
7 */
8
9 /* TODO: split this into multiple tests! ObLifetime, ObHandle, ObName, ... */
10
11 #include <kmt_test.h>
12
13 //#define NDEBUG
14 #include <debug.h>
15
16 #define CheckObject(Handle, PCount, HCount) do \
17 { \
18 PUBLIC_OBJECT_BASIC_INFORMATION ObjectInfo; \
19 Status = ZwQueryObject(Handle, ObjectBasicInformation, \
20 &ObjectInfo, sizeof ObjectInfo, NULL); \
21 ok_eq_hex(Status, STATUS_SUCCESS); \
22 ok_eq_ulong(ObjectInfo.PointerCount, PCount); \
23 ok_eq_ulong(ObjectInfo.HandleCount, HCount); \
24 } while (0)
25
26 #define NUM_OBTYPES 5
27
28 typedef struct _MY_OBJECT1
29 {
30 ULONG Something1;
31 } MY_OBJECT1, *PMY_OBJECT1;
32
33 typedef struct _MY_OBJECT2
34 {
35 ULONG Something1;
36 ULONG SomeLong[10];
37 } MY_OBJECT2, *PMY_OBJECT2;
38
39 static POBJECT_TYPE ObTypes[NUM_OBTYPES];
40 static UNICODE_STRING ObTypeName[NUM_OBTYPES];
41 static UNICODE_STRING ObName[NUM_OBTYPES];
42 static OBJECT_TYPE_INITIALIZER ObTypeInitializer[NUM_OBTYPES];
43 static UNICODE_STRING ObDirectoryName;
44 static OBJECT_ATTRIBUTES ObDirectoryAttributes;
45 static OBJECT_ATTRIBUTES ObAttributes[NUM_OBTYPES];
46 static PVOID ObBody[NUM_OBTYPES];
47 static HANDLE ObHandle1[NUM_OBTYPES];
48 static HANDLE ObHandle2[NUM_OBTYPES];
49 static HANDLE DirectoryHandle;
50
51 static USHORT DumpCount, OpenCount, CloseCount, DeleteCount,
52 ParseCount, OkayToCloseCount, QueryNameCount;
53
54 static
55 VOID
56 NTAPI
57 DumpProc(
58 IN PVOID Object,
59 IN POB_DUMP_CONTROL DumpControl)
60 {
61 DPRINT("DumpProc() called\n");
62 DumpCount++;
63 }
64
65 static
66 NTSTATUS
67 NTAPI
68 OpenProc(
69 IN OB_OPEN_REASON OpenReason,
70 IN PEPROCESS Process,
71 IN PVOID Object,
72 IN ACCESS_MASK GrantedAccess,
73 IN ULONG HandleCount)
74 {
75 DPRINT("OpenProc() 0x%p, OpenReason %d, HandleCount %lu, AccessMask 0x%lX\n",
76 Object, OpenReason, HandleCount, GrantedAccess);
77 OpenCount++;
78 return STATUS_SUCCESS;
79 }
80
81 static
82 VOID
83 NTAPI
84 CloseProc(
85 IN PEPROCESS Process,
86 IN PVOID Object,
87 IN ACCESS_MASK GrantedAccess,
88 IN ULONG ProcessHandleCount,
89 IN ULONG SystemHandleCount)
90 {
91 DPRINT("CloseProc() 0x%p, ProcessHandleCount %lu, SystemHandleCount %lu, AccessMask 0x%lX\n",
92 Object, ProcessHandleCount, SystemHandleCount, GrantedAccess);
93 CloseCount++;
94 }
95
96 static
97 VOID
98 NTAPI
99 DeleteProc(
100 IN PVOID Object)
101 {
102 DPRINT("DeleteProc() 0x%p\n", Object);
103 DeleteCount++;
104 }
105
106 static
107 NTSTATUS
108 NTAPI
109 ParseProc(
110 IN PVOID ParseObject,
111 IN PVOID ObjectType,
112 IN OUT PACCESS_STATE AccessState,
113 IN KPROCESSOR_MODE AccessMode,
114 IN ULONG Attributes,
115 IN OUT PUNICODE_STRING CompleteName,
116 IN OUT PUNICODE_STRING RemainingName,
117 IN OUT PVOID Context OPTIONAL,
118 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
119 OUT PVOID *Object)
120 {
121 DPRINT("ParseProc() called\n");
122 *Object = NULL;
123
124 ParseCount++;
125 return STATUS_OBJECT_NAME_NOT_FOUND;//STATUS_SUCCESS;
126 }
127
128 #if 0
129 static
130 NTSTATUS
131 NTAPI
132 OkayToCloseProc(
133 IN PEPROCESS Process OPTIONAL,
134 IN PVOID Object,
135 IN HANDLE Handle,
136 IN KPROCESSOR_MODE AccessMode)
137 {
138 DPRINT("OkayToCloseProc() 0x%p, Handle 0x%p, AccessMask 0x%lX\n",
139 Object, Handle, AccessMode);
140 OkayToCloseCount++;
141 return STATUS_SUCCESS;
142 }
143
144 static
145 NTSTATUS
146 NTAPI
147 QueryNameProc(
148 IN PVOID Object,
149 IN BOOLEAN HasObjectName,
150 OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
151 IN ULONG Length,
152 OUT PULONG ReturnLength,
153 IN KPROCESSOR_MODE AccessMode)
154 {
155 DPRINT("QueryNameProc() 0x%p, HasObjectName %d, Len %lu, AccessMask 0x%lX\n",
156 Object, HasObjectName, Length, AccessMode);
157 QueryNameCount++;
158
159 ObjectNameInfo = NULL;
160 ReturnLength = 0;
161 return STATUS_OBJECT_NAME_NOT_FOUND;
162 }
163 #endif /* 0 */
164
165 static
166 VOID
167 ObtCreateObjectTypes(VOID)
168 {
169 INT i;
170 NTSTATUS Status;
171 WCHAR Name[15];
172
173 for (i = 0; i < NUM_OBTYPES; i++)
174 {
175 Status = RtlStringCbPrintfW(Name, sizeof Name, L"MyObjectType%x", i);
176 ASSERT(NT_SUCCESS(Status));
177 RtlInitUnicodeString(&ObTypeName[i], Name);
178
179 RtlZeroMemory(&ObTypeInitializer[i], sizeof ObTypeInitializer[i]);
180 ObTypeInitializer[i].Length = sizeof ObTypeInitializer[i];
181 ObTypeInitializer[i].PoolType = NonPagedPool;
182 ObTypeInitializer[i].MaintainHandleCount = TRUE;
183 ObTypeInitializer[i].ValidAccessMask = OBJECT_TYPE_ALL_ACCESS;
184
185 // Test for invalid parameter
186 // FIXME: Make it more exact, to see which params Win2k3 checks
187 // existence of
188 Status = ObCreateObjectType(&ObTypeName[i], &ObTypeInitializer[i], NULL, &ObTypes[i]);
189 ok_eq_hex(Status, STATUS_INVALID_PARAMETER);
190
191 ObTypeInitializer[i].CloseProcedure = CloseProc;
192 ObTypeInitializer[i].DeleteProcedure = DeleteProc;
193 ObTypeInitializer[i].DumpProcedure = DumpProc;
194 ObTypeInitializer[i].OpenProcedure = OpenProc;
195 ObTypeInitializer[i].ParseProcedure = ParseProc;
196 //ObTypeInitializer[i].OkayToCloseProcedure = OkayToCloseProc;
197 //ObTypeInitializer[i].QueryNameProcedure = QueryNameProc;
198 //ObTypeInitializer[i].SecurityProcedure = SecurityProc;
199
200 Status = ObCreateObjectType(&ObTypeName[i], &ObTypeInitializer[i], NULL, &ObTypes[i]);
201 ok_eq_hex(Status, STATUS_SUCCESS);
202 }
203 }
204
205 static
206 VOID
207 ObtCreateDirectory(VOID)
208 {
209 NTSTATUS Status;
210
211 RtlInitUnicodeString(&ObDirectoryName, L"\\ObtDirectory");
212 InitializeObjectAttributes(&ObDirectoryAttributes, &ObDirectoryName, OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, NULL, NULL);
213 Status = ZwCreateDirectoryObject(&DirectoryHandle, DELETE, &ObDirectoryAttributes);
214 ok_eq_hex(Status, STATUS_SUCCESS);
215 CheckObject(DirectoryHandle, 3LU, 1LU);
216 }
217
218 #define CheckCounts(Open, Close, Delete, Parse, OkayToClose, QueryName) do \
219 { \
220 ok_eq_uint(OpenCount, Open); \
221 ok_eq_uint(CloseCount, Close); \
222 ok_eq_uint(DeleteCount, Delete); \
223 ok_eq_uint(ParseCount, Parse); \
224 ok_eq_uint(OkayToCloseCount, OkayToClose); \
225 ok_eq_uint(QueryNameCount, QueryName); \
226 } while (0)
227
228 #define SaveCounts(Open, Close, Delete, Parse, OkayToClose, QueryName) do \
229 { \
230 Open = OpenCount; \
231 Close = CloseCount; \
232 Delete = DeleteCount; \
233 Parse = ParseCount; \
234 OkayToClose = OkayToCloseCount; \
235 QueryName = QueryNameCount; \
236 } while (0)
237
238 /* TODO: make this the same as NUM_OBTYPES */
239 #define NUM_OBTYPES2 2
240 static
241 VOID
242 ObtCreateObjects(VOID)
243 {
244 PVOID ObBody1[2] = { NULL };
245 NTSTATUS Status;
246 WCHAR Name[NUM_OBTYPES2][MAX_PATH];
247 USHORT OpenSave, CloseSave, DeleteSave, ParseSave, OkayToCloseSave, QueryNameSave;
248 INT i;
249 ACCESS_MASK Access[NUM_OBTYPES2] = { STANDARD_RIGHTS_ALL, GENERIC_ALL };
250 ULONG ObjectSize[NUM_OBTYPES2] = { sizeof(MY_OBJECT1), sizeof(MY_OBJECT2) };
251
252 // Create two objects
253 for (i = 0; i < NUM_OBTYPES2; ++i)
254 {
255 ASSERT(sizeof Name[i] == MAX_PATH * sizeof(WCHAR));
256 Status = RtlStringCbPrintfW(Name[i], sizeof Name[i], L"\\ObtDirectory\\MyObject%d", i + 1);
257 ASSERT(Status == STATUS_SUCCESS);
258 RtlInitUnicodeString(&ObName[i], Name[i]);
259 InitializeObjectAttributes(&ObAttributes[i], &ObName[i], OBJ_CASE_INSENSITIVE, NULL, NULL);
260 }
261
262 for (i = 0; i < NUM_OBTYPES2; ++i)
263 {
264 Status = ObCreateObject(KernelMode, ObTypes[i], &ObAttributes[i], KernelMode, NULL, ObjectSize[i], 0L, 0L, &ObBody[i]);
265 ok_eq_hex(Status, STATUS_SUCCESS);
266 }
267
268 SaveCounts(OpenSave, CloseSave, DeleteSave, ParseSave, OkayToCloseSave, QueryNameSave);
269
270 // Insert them
271 for (i = 0; i < NUM_OBTYPES2; ++i)
272 {
273 Status = ObInsertObject(ObBody[i], NULL, Access[i], 0, &ObBody[i], &ObHandle1[i]);
274 ok_eq_hex(Status, STATUS_SUCCESS);
275 ok(ObBody[i] != NULL, "Object body = NULL\n");
276 ok(ObHandle1[i] != NULL, "Handle = NULL\n");
277 CheckObject(ObHandle1[i], 3LU, 1LU);
278 CheckCounts(OpenSave + 1, CloseSave, DeleteSave, ParseSave, OkayToCloseSave, QueryNameSave);
279 SaveCounts(OpenSave, CloseSave, DeleteSave, ParseSave, OkayToCloseSave, QueryNameSave);
280 }
281
282 // Now create an object of type 0, of the same name and expect it to fail
283 // inserting, but success creation
284 ok_eq_wstr(ObName[0].Buffer, L"\\ObtDirectory\\MyObject1");
285 RtlInitUnicodeString(&ObName[0], L"\\ObtDirectory\\MyObject1");
286 InitializeObjectAttributes(&ObAttributes[0], &ObName[0], OBJ_OPENIF, NULL, NULL);
287
288 Status = ObCreateObject(KernelMode, ObTypes[0], &ObAttributes[0], KernelMode, NULL, sizeof(MY_OBJECT1), 0L, 0L, &ObBody1[0]);
289 ok_eq_hex(Status, STATUS_SUCCESS);
290 CheckCounts(OpenSave, CloseSave, DeleteSave, ParseSave, OkayToCloseSave, QueryNameSave);
291
292 Status = ObInsertObject(ObBody1[0], NULL, GENERIC_ALL, 0, &ObBody1[1], &ObHandle2[0]);
293 ok_eq_hex(Status, STATUS_ACCESS_DENIED/*STATUS_OBJECT_NAME_EXISTS*/);
294 ok_eq_pointer(ObBody[0], ObBody1[1]);
295 ok(ObHandle2[0] != NULL, "NULL handle returned\n");
296
297 DPRINT("%d %d %d %d %d %d %d\n", DumpCount, OpenCount, CloseCount, DeleteCount, ParseCount, OkayToCloseCount, QueryNameCount);
298 CheckCounts(OpenSave + 1, CloseSave, DeleteSave + 1, ParseSave, OkayToCloseSave, QueryNameSave);
299 SaveCounts(OpenSave, CloseSave, DeleteSave, ParseSave, OkayToCloseSave, QueryNameSave);
300
301 // Close its handle
302 if (!skip(ObHandle2[0] && ObHandle2[0] != INVALID_HANDLE_VALUE, "Nothing to close\n"))
303 {
304 Status = ZwClose(ObHandle2[0]);
305 ok_eq_hex(Status, STATUS_SUCCESS);
306 CheckCounts(OpenSave, CloseSave + 1, DeleteSave, ParseSave, OkayToCloseSave, QueryNameSave);
307 SaveCounts(OpenSave, CloseSave, DeleteSave, ParseSave, OkayToCloseSave, QueryNameSave);
308 }
309
310 // Object referenced 2 times:
311 // 1) ObInsertObject
312 // 2) AdditionalReferences
313 if (ObBody1[1])
314 ObDereferenceObject(ObBody1[1]);
315 //DPRINT1("%d %d %d %d %d %d %d\n", DumpCount, OpenCount, CloseCount, DeleteCount, ParseCount, OkayToCloseCount, QueryNameCount);
316 CheckCounts(OpenSave, CloseSave, DeleteSave, ParseSave, OkayToCloseSave, QueryNameSave);
317 }
318
319 static
320 VOID
321 ObtClose(
322 BOOLEAN Clean,
323 BOOLEAN AlternativeMethod)
324 {
325 PVOID DirObject;
326 NTSTATUS Status;
327 PVOID TypeObject;
328 INT i;
329 UNICODE_STRING ObPathName[NUM_OBTYPES];
330 WCHAR Name[MAX_PATH];
331
332 // Close what we have opened and free what we allocated
333 for (i = 0; i < NUM_OBTYPES2; ++i)
334 {
335 if (ObBody[i])
336 {
337 if (ObHandle1[i]) CheckObject(ObHandle1[i], 3LU, 1LU);
338 ObDereferenceObject(ObBody[i]);
339 if (ObHandle1[i]) CheckObject(ObHandle1[i], 2LU, 1LU);
340 ObBody[i] = NULL;
341 }
342 if (ObHandle1[i])
343 {
344 ZwClose(ObHandle1[i]);
345 ObHandle1[i] = NULL;
346 }
347 if (ObHandle2[i])
348 {
349 ZwClose(ObHandle2[i]);
350 ObHandle2[i] = NULL;
351 }
352 }
353
354 if (!Clean)
355 return;
356
357 // Now we have to get rid of a directory object
358 // Since it is permanent, we have to firstly make it temporary
359 // and only then kill
360 // (this procedure is described in DDK)
361 if (DirectoryHandle && DirectoryHandle != INVALID_HANDLE_VALUE)
362 {
363 CheckObject(DirectoryHandle, 3LU, 1LU);
364 Status = ObReferenceObjectByHandle(DirectoryHandle, 0L, NULL, KernelMode, &DirObject, NULL);
365 ok_eq_hex(Status, STATUS_SUCCESS);
366 CheckObject(DirectoryHandle, 4LU, 1LU);
367
368 Status = ZwMakeTemporaryObject(DirectoryHandle);
369 ok_eq_hex(Status, STATUS_SUCCESS);
370 CheckObject(DirectoryHandle, 4LU, 1LU);
371
372 // Dereference 2 times - first for just previous referencing
373 // and 2nd time for creation of permanent object itself
374 ObDereferenceObject(DirObject);
375 CheckObject(DirectoryHandle, 3LU, 1LU);
376 ObDereferenceObject(DirObject);
377 CheckObject(DirectoryHandle, 2LU, 1LU);
378
379 Status = ZwClose(DirectoryHandle);
380 ok_eq_hex(Status, STATUS_SUCCESS);
381 }
382
383 // Now delete the last piece - object types
384 // In fact, it's weird to get rid of object types, especially the way,
385 // how it's done in the commented section below
386 if (!AlternativeMethod)
387 {
388 for (i = 0; i < NUM_OBTYPES; ++i)
389 if (ObTypes[i])
390 {
391 ObDereferenceObject(ObTypes[i]);
392 ObTypes[i] = NULL;
393 }
394 }
395 else
396 {
397 for (i = 0; i < NUM_OBTYPES; ++i)
398 {
399 if (ObTypes[i])
400 {
401 Status = RtlStringCbPrintfW(Name, sizeof Name, L"\\ObjectTypes\\MyObjectType%d", i);
402 RtlInitUnicodeString(&ObPathName[0], Name);
403 Status = ObReferenceObjectByName(&ObPathName[i], OBJ_CASE_INSENSITIVE, NULL, 0L, NULL, KernelMode, NULL, &TypeObject);
404
405 ObDereferenceObject(TypeObject);
406 ObDereferenceObject(TypeObject);
407 DPRINT("Reference Name %wZ = %p, ObTypes[%d] = %p\n",
408 ObPathName[i], TypeObject, i, ObTypes[i]);
409 ObTypes[i] = NULL;
410 }
411 }
412 }
413 }
414
415 #if 0
416 static
417 VOID
418 ObtReferenceTests(VOID)
419 {
420 INT i;
421 NTSTATUS Status;
422 UNICODE_STRING ObPathName[NUM_OBTYPES];
423
424 // Reference them by handle
425 for (i = 0; i < NUM_OBTYPES2; i++)
426 {
427 CheckObject(ObHandle1[i], 2LU, 1LU);
428 Status = ObReferenceObjectByHandle(ObHandle1[i], 0L, ObTypes[i], KernelMode, &ObBody[i], NULL);
429 ok_eq_hex(Status, STATUS_SUCCESS);
430 CheckObject(ObHandle1[i], 3LU, 1LU);
431 DPRINT("Ref by handle %lx = %p\n", ObHandle1[i], ObBody[i]);
432 }
433
434 // Reference them by pointer
435 for (i = 0; i < NUM_OBTYPES2; i++)
436 {
437 CheckObject(ObHandle1[i], 3LU, 1LU);
438 Status = ObReferenceObjectByPointer(ObBody[i], 0L, ObTypes[i], KernelMode);
439 CheckObject(ObHandle1[i], 4LU, 1LU);
440 ok_eq_hex(Status, STATUS_SUCCESS);
441 }
442
443 // Reference them by name
444 RtlInitUnicodeString(&ObPathName[0], L"\\ObtDirectory\\MyObject1");
445 RtlInitUnicodeString(&ObPathName[1], L"\\ObtDirectory\\MyObject2");
446
447 #if 0
448 for (i = 0; i < NUM_OBTYPES2; i++)
449 {
450 Status = ObReferenceObjectByName(&ObPathName[i], OBJ_CASE_INSENSITIVE, NULL, 0L, ObTypes[i], KernelMode, NULL, &ObBody[i]);
451 DPRINT("Ref by name %wZ = %p\n", &ObPathName[i], ObBody[i]);
452 }
453 #endif
454
455 // Dereference now all of them
456
457 for (i = 0; i < NUM_OBTYPES2; ++i)
458 if (!skip(ObBody[i] != NULL, "Object pointer is NULL\n"))
459 {
460 CheckObject(ObHandle1[i], 4LU, 1LU);
461 // For ObInsertObject, AdditionalReference
462 //ObDereferenceObject(ObBody[i]);
463 // For ByName
464 //ObDereferenceObject(ObBody[i]);
465 // For ByPointer
466 ObDereferenceObject(ObBody[i]);
467 CheckObject(ObHandle1[i], 3LU, 1LU);
468 // For ByHandle
469 ObDereferenceObject(ObBody[i]);
470 CheckObject(ObHandle1[i], 2LU, 1LU);
471 }
472 }
473 #endif /* 0 */
474
475 static
476 VOID
477 TestObjectType(
478 IN BOOLEAN Clean)
479 {
480 DumpCount = 0; OpenCount = 0; CloseCount = 0;
481 DeleteCount = 0; ParseCount = 0;
482
483 ObtCreateObjectTypes();
484 DPRINT("ObtCreateObjectTypes() done\n");
485
486 ObtCreateDirectory();
487 DPRINT("ObtCreateDirectory() done\n");
488
489 ObtCreateObjects();
490 DPRINT("ObtCreateObjects() done\n");
491
492 //ObtReferenceTests();
493
494 ObtClose(Clean, FALSE);
495 }
496
497 START_TEST(ObType)
498 {
499 TestObjectType(TRUE);
500 }
501
502 /* run this to see the objects created in user mode */
503 START_TEST(ObTypeNoClean)
504 {
505 TestObjectType(FALSE);
506 trace("Cleanup skipped as requested! Run ObTypeClean to clean up\n");
507 }
508
509 /* run this to clean up after ObTypeNoClean */
510 START_TEST(ObTypeClean)
511 {
512 ObtClose(TRUE, FALSE);
513 trace("Cleanup done\n");
514 }