reactos/modules/rostests/kmtests/ntos_ob/ObType.c

449 lines
14 KiB
C

/*
* 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 <aleksey@reactos.org>
* Thomas Faber <thomas.faber@reactos.org>
*/
/* TODO: split this into multiple tests! ObLife, ObHandle, ObName, ... */
#include <kmt_test.h>
#define NDEBUG
#include <debug.h>
#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);
}