/* * PROJECT: ReactOS kernel-mode tests * LICENSE: LGPLv2+ - See COPYING.LIB in the top level directory * PURPOSE: Kernel-Mode Test Suite Ob Regressions KM-Test * PROGRAMMER: Aleksey Bragin * Thomas Faber */ /* TODO: split this into multiple tests! ObLife, ObHandle, ObName, ... */ #include #define NDEBUG #include #define CheckObject(Handle, Pointers, Handles) do \ { \ PUBLIC_OBJECT_BASIC_INFORMATION ObjectInfo; \ Status = ZwQueryObject(Handle, ObjectBasicInformation, \ &ObjectInfo, sizeof ObjectInfo, NULL); \ ok_eq_hex(Status, STATUS_SUCCESS); \ ok_eq_ulong(ObjectInfo.PointerCount, Pointers); \ ok_eq_ulong(ObjectInfo.HandleCount, Handles); \ } while (0) #define NUM_OBTYPES 5 typedef struct _MY_OBJECT1 { ULONG Something1; } MY_OBJECT1, *PMY_OBJECT1; typedef struct _MY_OBJECT2 { ULONG Something1; ULONG SomeLong[10]; } MY_OBJECT2, *PMY_OBJECT2; static POBJECT_TYPE ObTypes[NUM_OBTYPES]; static UNICODE_STRING ObTypeName[NUM_OBTYPES]; static UNICODE_STRING ObName[NUM_OBTYPES]; static OBJECT_TYPE_INITIALIZER ObTypeInitializer[NUM_OBTYPES]; static UNICODE_STRING ObDirectoryName; static OBJECT_ATTRIBUTES ObDirectoryAttributes; static OBJECT_ATTRIBUTES ObAttributes[NUM_OBTYPES]; static PVOID ObBody[NUM_OBTYPES]; static HANDLE ObHandle1[NUM_OBTYPES]; static HANDLE DirectoryHandle; typedef struct _COUNTS { USHORT Dump; USHORT Open; USHORT Close; USHORT Delete; USHORT Parse; USHORT OkayToClose; USHORT QueryName; } COUNTS, *PCOUNTS; static COUNTS Counts; static VOID NTAPI DumpProc( IN PVOID Object, IN POB_DUMP_CONTROL DumpControl) { DPRINT("DumpProc() called\n"); ++Counts.Dump; } static NTSTATUS NTAPI OpenProc( IN OB_OPEN_REASON OpenReason, IN PEPROCESS Process, IN PVOID Object, IN ACCESS_MASK GrantedAccess, IN ULONG HandleCount) { DPRINT("OpenProc() 0x%p, OpenReason %d, HandleCount %lu, AccessMask 0x%lX\n", Object, OpenReason, HandleCount, GrantedAccess); ++Counts.Open; return STATUS_SUCCESS; } static VOID NTAPI CloseProc( IN PEPROCESS Process, IN PVOID Object, IN ACCESS_MASK GrantedAccess, IN ULONG ProcessHandleCount, IN ULONG SystemHandleCount) { DPRINT("CloseProc() 0x%p, ProcessHandleCount %lu, SystemHandleCount %lu, AccessMask 0x%lX\n", Object, ProcessHandleCount, SystemHandleCount, GrantedAccess); ++Counts.Close; } static VOID NTAPI DeleteProc( IN PVOID Object) { DPRINT("DeleteProc() 0x%p\n", Object); ++Counts.Delete; } static NTSTATUS NTAPI ParseProc( IN PVOID ParseObject, IN PVOID ObjectType, IN OUT PACCESS_STATE AccessState, IN KPROCESSOR_MODE AccessMode, IN ULONG Attributes, IN OUT PUNICODE_STRING CompleteName, IN OUT PUNICODE_STRING RemainingName, IN OUT PVOID Context OPTIONAL, IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL, OUT PVOID *Object) { DPRINT("ParseProc() called\n"); *Object = NULL; ++Counts.Parse; return STATUS_OBJECT_NAME_NOT_FOUND;//STATUS_SUCCESS; } static BOOLEAN NTAPI OkayToCloseProc( IN PEPROCESS Process OPTIONAL, IN PVOID Object, IN HANDLE Handle, IN KPROCESSOR_MODE AccessMode) { DPRINT("OkayToCloseProc() 0x%p, Handle 0x%p, AccessMask 0x%lX\n", Object, Handle, AccessMode); ++Counts.OkayToClose; return TRUE; } static NTSTATUS NTAPI QueryNameProc( IN PVOID Object, IN BOOLEAN HasObjectName, OUT POBJECT_NAME_INFORMATION ObjectNameInfo, IN ULONG Length, OUT PULONG ReturnLength, IN KPROCESSOR_MODE AccessMode) { DPRINT("QueryNameProc() 0x%p, HasObjectName %d, Len %lu, AccessMask 0x%lX\n", Object, HasObjectName, Length, AccessMode); ++Counts.QueryName; ObjectNameInfo = NULL; ReturnLength = 0; return STATUS_OBJECT_NAME_NOT_FOUND; } static VOID ObtCreateObjectTypes(VOID) { INT i; NTSTATUS Status; struct { WCHAR DirectoryName[sizeof "\\ObjectTypes\\" - 1]; WCHAR TypeName[15]; } Name; OBJECT_ATTRIBUTES ObjectAttributes; HANDLE ObjectTypeHandle; UNICODE_STRING ObjectPath; RtlCopyMemory(&Name.DirectoryName, L"\\ObjectTypes\\", sizeof Name.DirectoryName); for (i = 0; i < NUM_OBTYPES; ++i) { Status = RtlStringCbPrintfW(Name.TypeName, sizeof Name.TypeName, L"MyObjectType%x", i); ASSERT(NT_SUCCESS(Status)); RtlInitUnicodeString(&ObTypeName[i], Name.TypeName); DPRINT("Creating object type %wZ\n", &ObTypeName[i]); RtlZeroMemory(&ObTypeInitializer[i], sizeof ObTypeInitializer[i]); ObTypeInitializer[i].Length = sizeof ObTypeInitializer[i]; ObTypeInitializer[i].PoolType = NonPagedPool; ObTypeInitializer[i].MaintainHandleCount = TRUE; ObTypeInitializer[i].ValidAccessMask = OBJECT_TYPE_ALL_ACCESS; // Test for invalid parameter // FIXME: Make it more exact, to see which params Win2k3 checks // existence of Status = ObCreateObjectType(&ObTypeName[i], &ObTypeInitializer[i], NULL, &ObTypes[i]); ok_eq_hex(Status, STATUS_INVALID_PARAMETER); ObTypeInitializer[i].CloseProcedure = CloseProc; ObTypeInitializer[i].DeleteProcedure = DeleteProc; ObTypeInitializer[i].DumpProcedure = DumpProc; ObTypeInitializer[i].OpenProcedure = OpenProc; ObTypeInitializer[i].ParseProcedure = ParseProc; ObTypeInitializer[i].OkayToCloseProcedure = OkayToCloseProc; ObTypeInitializer[i].QueryNameProcedure = QueryNameProc; //ObTypeInitializer[i].SecurityProcedure = SecurityProc; Status = ObCreateObjectType(&ObTypeName[i], &ObTypeInitializer[i], NULL, &ObTypes[i]); if (Status == STATUS_OBJECT_NAME_COLLISION) { /* as we cannot delete the object types, get a pointer if they * already exist */ RtlInitUnicodeString(&ObjectPath, Name.DirectoryName); InitializeObjectAttributes(&ObjectAttributes, &ObjectPath, OBJ_KERNEL_HANDLE, NULL, NULL); Status = ObOpenObjectByName(&ObjectAttributes, NULL, KernelMode, NULL, 0, NULL, &ObjectTypeHandle); ok_eq_hex(Status, STATUS_SUCCESS); ok(ObjectTypeHandle != NULL, "ObjectTypeHandle = NULL\n"); if (!skip(Status == STATUS_SUCCESS && ObjectTypeHandle, "No handle\n")) { Status = ObReferenceObjectByHandle(ObjectTypeHandle, 0, NULL, KernelMode, (PVOID)&ObTypes[i], NULL); ok_eq_hex(Status, STATUS_SUCCESS); if (!skip(Status == STATUS_SUCCESS && ObTypes[i], "blah\n")) { ObTypes[i]->TypeInfo.CloseProcedure = CloseProc; ObTypes[i]->TypeInfo.DeleteProcedure = DeleteProc; ObTypes[i]->TypeInfo.DumpProcedure = DumpProc; ObTypes[i]->TypeInfo.OpenProcedure = OpenProc; ObTypes[i]->TypeInfo.ParseProcedure = ParseProc; ObTypes[i]->TypeInfo.OkayToCloseProcedure = OkayToCloseProc; ObTypes[i]->TypeInfo.QueryNameProcedure = QueryNameProc; } Status = ZwClose(ObjectTypeHandle); } } ok_eq_hex(Status, STATUS_SUCCESS); ok(ObTypes[i] != NULL, "ObType = NULL\n"); } } static VOID ObtCreateDirectory(VOID) { NTSTATUS Status; RtlInitUnicodeString(&ObDirectoryName, L"\\ObtDirectory"); InitializeObjectAttributes(&ObDirectoryAttributes, &ObDirectoryName, OBJ_KERNEL_HANDLE | OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, NULL, NULL); Status = ZwCreateDirectoryObject(&DirectoryHandle, DELETE, &ObDirectoryAttributes); ok_eq_hex(Status, STATUS_SUCCESS); CheckObject(DirectoryHandle, 3LU, 1LU); } #define CheckCounts(OpenCount, CloseCount, DeleteCount, ParseCount, \ OkayToCloseCount, QueryNameCount) do \ { \ ok_eq_uint(Counts.Open, OpenCount); \ ok_eq_uint(Counts.Close, CloseCount); \ ok_eq_uint(Counts.Delete, DeleteCount); \ ok_eq_uint(Counts.Parse, ParseCount); \ ok_eq_uint(Counts.OkayToClose, OkayToCloseCount); \ ok_eq_uint(Counts.QueryName, QueryNameCount); \ } while (0) #define SaveCounts(Save) memcpy(&Save, &Counts, sizeof Counts) /* TODO: make this the same as NUM_OBTYPES */ #define NUM_OBTYPES2 2 static VOID ObtCreateObjects(VOID) { NTSTATUS Status; WCHAR Name[NUM_OBTYPES2][MAX_PATH]; COUNTS SaveCounts; INT i; ACCESS_MASK Access[NUM_OBTYPES2] = { STANDARD_RIGHTS_ALL, GENERIC_ALL }; ULONG ObjectSize[NUM_OBTYPES2] = { sizeof(MY_OBJECT1), sizeof(MY_OBJECT2) }; // Create two objects for (i = 0; i < NUM_OBTYPES2; ++i) { ASSERT(sizeof Name[i] == MAX_PATH * sizeof(WCHAR)); Status = RtlStringCbPrintfW(Name[i], sizeof Name[i], L"\\ObtDirectory\\MyObject%d", i + 1); ASSERT(Status == STATUS_SUCCESS); RtlInitUnicodeString(&ObName[i], Name[i]); InitializeObjectAttributes(&ObAttributes[i], &ObName[i], OBJ_CASE_INSENSITIVE, NULL, NULL); } CheckObject(DirectoryHandle, 3LU, 1LU); for (i = 0; i < NUM_OBTYPES2; ++i) { Status = ObCreateObject(KernelMode, ObTypes[i], &ObAttributes[i], KernelMode, NULL, ObjectSize[i], 0L, 0L, &ObBody[i]); ok_eq_hex(Status, STATUS_SUCCESS); } SaveCounts(SaveCounts); // Insert them for (i = 0; i < NUM_OBTYPES2; ++i) { CheckObject(DirectoryHandle, 3LU + i, 1LU); Status = ObInsertObject(ObBody[i], NULL, Access[i], 0, &ObBody[i], &ObHandle1[i]); ok_eq_hex(Status, STATUS_SUCCESS); ok(ObBody[i] != NULL, "Object body = NULL\n"); ok(ObHandle1[i] != NULL, "Handle = NULL\n"); CheckObject(ObHandle1[i], 3LU, 1LU); CheckCounts(SaveCounts.Open + 1, SaveCounts.Close, SaveCounts.Delete, SaveCounts.Parse, SaveCounts.OkayToClose, SaveCounts.QueryName); SaveCounts(SaveCounts); CheckObject(DirectoryHandle, 4LU + i, 1LU); } //DPRINT1("%d %d %d %d %d %d %d\n", DumpCount, OpenCount, CloseCount, DeleteCount, ParseCount, OkayToCloseCount, QueryNameCount); CheckCounts(SaveCounts.Open, SaveCounts.Close, SaveCounts.Delete, SaveCounts.Parse, SaveCounts.OkayToClose, SaveCounts.QueryName); } static VOID ObtClose( BOOLEAN Clean, BOOLEAN AlternativeMethod) { NTSTATUS Status; LONG_PTR Ret; PVOID TypeObject; INT i; UNICODE_STRING ObPathName[NUM_OBTYPES]; WCHAR Name[MAX_PATH]; // Close what we have opened and free what we allocated for (i = 0; i < NUM_OBTYPES2; ++i) { if (!skip(ObBody[i] != NULL, "Nothing to dereference\n")) { if (ObHandle1[i]) CheckObject(ObHandle1[i], 3LU, 1LU); Ret = ObReferenceObject(ObBody[i]); if (ObHandle1[i]) CheckObject(ObHandle1[i], 4LU, 1LU); Ret = ObDereferenceObject(ObBody[i]); ok_eq_longptr(Ret, (LONG_PTR)2); if (ObHandle1[i]) CheckObject(ObHandle1[i], 3LU, 1LU); ObBody[i] = NULL; } if (!skip(ObHandle1[i] != NULL, "Nothing to close\n")) { Status = ZwClose(ObHandle1[i]); ok_eq_hex(Status, STATUS_SUCCESS); ObHandle1[i] = NULL; } } if (skip(Clean, "Not cleaning up, as requested. Use ObTypeClean to clean up\n")) return; // Now we have to get rid of a directory object // Since it is permanent, we have to firstly make it temporary // and only then kill // (this procedure is described in DDK) if (!skip(DirectoryHandle != NULL, "No directory handle\n")) { CheckObject(DirectoryHandle, 3LU, 1LU); Status = ZwMakeTemporaryObject(DirectoryHandle); ok_eq_hex(Status, STATUS_SUCCESS); CheckObject(DirectoryHandle, 3LU, 1LU); Status = ZwClose(DirectoryHandle); ok_eq_hex(Status, STATUS_SUCCESS); } /* we don't delete the object types we created. It makes Windows unstable. * TODO: perhaps make it work in ROS anyway */ return; if (!AlternativeMethod) { for (i = 0; i < NUM_OBTYPES; ++i) if (!skip(ObTypes[i] != NULL, "No object type to delete\n")) { Ret = ObDereferenceObject(ObTypes[i]); ok_eq_longptr(Ret, (LONG_PTR)0); ObTypes[i] = NULL; } } else { for (i = 0; i < NUM_OBTYPES; ++i) { if (!skip(ObTypes[i] != NULL, "No object type to delete\n")) { Status = RtlStringCbPrintfW(Name, sizeof Name, L"\\ObjectTypes\\MyObjectType%d", i); RtlInitUnicodeString(&ObPathName[i], Name); Status = ObReferenceObjectByName(&ObPathName[i], OBJ_CASE_INSENSITIVE, NULL, 0L, NULL, KernelMode, NULL, &TypeObject); Ret = ObDereferenceObject(TypeObject); ok_eq_longptr(Ret, (LONG_PTR)2); Ret = ObDereferenceObject(TypeObject); ok_eq_longptr(Ret, (LONG_PTR)1); DPRINT("Reference Name %wZ = %p, ObTypes[%d] = %p\n", ObPathName[i], TypeObject, i, ObTypes[i]); ObTypes[i] = NULL; } } } } static VOID TestObjectType( IN BOOLEAN Clean) { RtlZeroMemory(&Counts, sizeof Counts); ObtCreateObjectTypes(); DPRINT("ObtCreateObjectTypes() done\n"); ObtCreateDirectory(); DPRINT("ObtCreateDirectory() done\n"); if (!skip(ObTypes[0] != NULL, "No object types!\n")) ObtCreateObjects(); DPRINT("ObtCreateObjects() done\n"); ObtClose(Clean, FALSE); } START_TEST(ObType) { TestObjectType(TRUE); } /* run this to see the objects created in user mode */ START_TEST(ObTypeNoClean) { TestObjectType(FALSE); } /* run this to clean up after ObTypeNoClean */ START_TEST(ObTypeClean) { ObtClose(TRUE, FALSE); }