mirror of
https://github.com/reactos/reactos.git
synced 2024-09-20 17:51:53 +00:00
424bbf064b
- After ObCreateObject+ObInsertObject a handle close is enough to destroy the object, so do not dereference it in addition. Fixes use after free. CORE-11474 svn path=/trunk/; revision=74375
449 lines
14 KiB
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);
|
|
}
|