mirror of
https://github.com/reactos/reactos.git
synced 2025-08-03 15:46:13 +00:00
[KMTESTS]
- Implement a mechanism for kmtests to retrieve information from user-mode. Patch by Nikolay Borisov (nib9 at aber dot ac dot uk). - Currently supported function: QueryVirtualMemory ROSTESTS-96 #resolve svn path=/trunk/; revision=58873
This commit is contained in:
parent
b74ef49f87
commit
4c309ccd7f
4 changed files with 341 additions and 0 deletions
|
@ -17,6 +17,12 @@
|
|||
#define IOCTL_KMTEST_SET_RESULTBUFFER \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA)
|
||||
|
||||
#define IOCTL_KMTEST_USERMODE_SEND_RESPONSE \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_IN_DIRECT, FILE_WRITE_DATA)
|
||||
|
||||
#define IOCTL_KMTEST_USERMODE_AWAIT_REQ \
|
||||
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_READ_DATA)
|
||||
|
||||
#define KMTEST_DEVICE_NAME L"Kmtest"
|
||||
#define KMTEST_DEVICE_DRIVER_PATH L"\\Device\\" KMTEST_DEVICE_NAME
|
||||
#define KMTEST_DEVICE_PATH L"\\\\.\\Global\\GLOBALROOT" KMTEST_DEVICE_DRIVER_PATH
|
||||
|
|
|
@ -38,6 +38,48 @@ typedef struct
|
|||
CHAR LogBuffer[ANYSIZE_ARRAY];
|
||||
} KMT_RESULTBUFFER, *PKMT_RESULTBUFFER;
|
||||
|
||||
#ifndef KMT_STANDALONE_DRIVER
|
||||
|
||||
/* usermode call-back mechanism */
|
||||
|
||||
/* list of supported operations */
|
||||
typedef enum _KMT_CALLBACK_INFORMATION_CLASS
|
||||
{
|
||||
QueryVirtualMemory
|
||||
} KMT_CALLBACK_INFORMATION_CLASS, *PKMT_CALLBACK_INFORMATION_CLASS;
|
||||
|
||||
/* TODO: "response" is a little generic */
|
||||
typedef union _KMT_RESPONSE
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION MemInfo;
|
||||
} KMT_RESPONSE, *PKMT_RESPONSE;
|
||||
|
||||
/* this struct is sent from driver to usermode */
|
||||
typedef struct _KMT_CALLBACK_REQUEST_PACKET
|
||||
{
|
||||
ULONG RequestId;
|
||||
KMT_CALLBACK_INFORMATION_CLASS OperationClass;
|
||||
PVOID Parameters;
|
||||
} KMT_CALLBACK_REQUEST_PACKET, *PKMT_CALLBACK_REQUEST_PACKET;
|
||||
|
||||
PKMT_RESPONSE KmtUserModeCallback(KMT_CALLBACK_INFORMATION_CLASS Operation, PVOID Parameters);
|
||||
VOID KmtFreeCallbackResponse(PKMT_RESPONSE Response);
|
||||
|
||||
//macro to simplify using the mechanism
|
||||
#define Test_NtQueryVirtualMemory(BaseAddress, Size, AllocationType, ProtectionType) \
|
||||
do { \
|
||||
PKMT_RESPONSE NtQueryTest = KmtUserModeCallback(QueryVirtualMemory, BaseAddress); \
|
||||
if (NtQueryTest != NULL) \
|
||||
{ \
|
||||
ok_eq_hex(NtQueryTest->MemInfo.Protect, ProtectionType); \
|
||||
ok_eq_hex(NtQueryTest->MemInfo.State, AllocationType); \
|
||||
ok_eq_size(NtQueryTest->MemInfo.RegionSize, Size); \
|
||||
KmtFreeCallbackResponse(NtQueryTest); \
|
||||
} \
|
||||
} while (0) \
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef KMT_STANDALONE_DRIVER
|
||||
#define KMT_KERNEL_MODE
|
||||
|
||||
|
|
|
@ -11,9 +11,74 @@
|
|||
#include <kmt_public.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <debug.h>
|
||||
|
||||
extern HANDLE KmtestHandle;
|
||||
|
||||
/**
|
||||
* @name KmtUserCallbackThread
|
||||
*
|
||||
* Thread routine which awaits callback requests from kernel-mode
|
||||
*
|
||||
* @return Win32 error code
|
||||
*/
|
||||
DWORD
|
||||
WINAPI
|
||||
KmtUserCallbackThread(
|
||||
PVOID Parameter)
|
||||
{
|
||||
DWORD Error = ERROR_SUCCESS;
|
||||
/* TODO: RequestPacket? */
|
||||
KMT_CALLBACK_REQUEST_PACKET RequestPacket;
|
||||
KMT_RESPONSE Response;
|
||||
DWORD BytesReturned;
|
||||
HANDLE LocalKmtHandle;
|
||||
|
||||
UNREFERENCED_PARAMETER(Parameter);
|
||||
|
||||
/* concurrent IoCtls on the same (non-overlapped) handle aren't possible,
|
||||
* so open a separate one.
|
||||
* For more info http://www.osronline.com/showthread.cfm?link=230782 */
|
||||
LocalKmtHandle = CreateFile(KMTEST_DEVICE_PATH, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if (LocalKmtHandle == INVALID_HANDLE_VALUE)
|
||||
error_goto(Error, cleanup);
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (!DeviceIoControl(LocalKmtHandle, IOCTL_KMTEST_USERMODE_AWAIT_REQ, NULL, 0, &RequestPacket, sizeof(RequestPacket), &BytesReturned, NULL))
|
||||
error_goto(Error, cleanup);
|
||||
ASSERT(BytesReturned == sizeof(RequestPacket));
|
||||
|
||||
switch (RequestPacket.OperationClass)
|
||||
{
|
||||
case QueryVirtualMemory:
|
||||
{
|
||||
SIZE_T InfoBufferSize = VirtualQuery(RequestPacket.Parameters, &Response.MemInfo, sizeof(Response.MemInfo));
|
||||
/* FIXME: an error is a valid result. That should go as a response to kernel mode instead of terminating the thread */
|
||||
if (InfoBufferSize == 0)
|
||||
error_goto(Error, cleanup);
|
||||
|
||||
if (!DeviceIoControl(LocalKmtHandle, IOCTL_KMTEST_USERMODE_SEND_RESPONSE, &RequestPacket.RequestId, sizeof(RequestPacket.RequestId), &Response, sizeof(Response), &BytesReturned, NULL))
|
||||
error_goto(Error, cleanup);
|
||||
ASSERT(BytesReturned == 0);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DPRINT1("Unrecognized user-mode callback request\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (LocalKmtHandle != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(LocalKmtHandle);
|
||||
|
||||
DPRINT("Callback handler dying! Error code %lu", Error);
|
||||
return Error;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @name KmtRunKernelTest
|
||||
*
|
||||
|
@ -28,12 +93,18 @@ DWORD
|
|||
KmtRunKernelTest(
|
||||
IN PCSTR TestName)
|
||||
{
|
||||
HANDLE CallbackThread;
|
||||
DWORD Error = ERROR_SUCCESS;
|
||||
DWORD BytesRead;
|
||||
|
||||
CallbackThread = CreateThread(NULL, 0, KmtUserCallbackThread, NULL, 0, NULL);
|
||||
|
||||
if (!DeviceIoControl(KmtestHandle, IOCTL_KMTEST_RUN_TEST, (PVOID)TestName, (DWORD)strlen(TestName), NULL, 0, &BytesRead, NULL))
|
||||
error(Error);
|
||||
|
||||
if (CallbackThread != NULL)
|
||||
CloseHandle(CallbackThread);
|
||||
|
||||
return Error;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,22 @@
|
|||
#define KMT_DEFINE_TEST_FUNCTIONS
|
||||
#include <kmt_test.h>
|
||||
|
||||
/* Usermode callback definitions */
|
||||
typedef struct _KMT_USER_WORK_ENTRY
|
||||
{
|
||||
LIST_ENTRY ListEntry;
|
||||
KEVENT WorkDoneEvent;
|
||||
KMT_CALLBACK_REQUEST_PACKET Request;
|
||||
PKMT_RESPONSE Response;
|
||||
} KMT_USER_WORK_ENTRY, *PKMT_USER_WORK_ENTRY;
|
||||
|
||||
typedef struct _KMT_USER_WORK_LIST
|
||||
{
|
||||
LIST_ENTRY ListHead;
|
||||
FAST_MUTEX Lock;
|
||||
KEVENT NewWorkEvent;
|
||||
} KMT_USER_WORK_LIST, *PKMT_USER_WORK_LIST;
|
||||
|
||||
/* Prototypes */
|
||||
DRIVER_INITIALIZE DriverEntry;
|
||||
static DRIVER_UNLOAD DriverUnload;
|
||||
|
@ -30,10 +46,13 @@ __drv_dispatchType(IRP_MJ_CLOSE)
|
|||
static DRIVER_DISPATCH DriverClose;
|
||||
__drv_dispatchType(IRP_MJ_DEVICE_CONTROL)
|
||||
static DRIVER_DISPATCH DriverIoControl;
|
||||
static VOID KmtCleanUsermodeCallbacks(VOID);
|
||||
|
||||
/* Globals */
|
||||
static PDEVICE_OBJECT MainDeviceObject;
|
||||
PDRIVER_OBJECT KmtDriverObject = NULL;
|
||||
static KMT_USER_WORK_LIST WorkList;
|
||||
static ULONG RequestId = 0;
|
||||
|
||||
/* Entry */
|
||||
/**
|
||||
|
@ -92,6 +111,10 @@ DriverEntry(
|
|||
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverClose;
|
||||
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DriverIoControl;
|
||||
|
||||
ExInitializeFastMutex(&WorkList.Lock);
|
||||
KeInitializeEvent(&WorkList.NewWorkEvent, NotificationEvent, FALSE);
|
||||
InitializeListHead(&WorkList.ListHead);
|
||||
|
||||
cleanup:
|
||||
if (MainDeviceObject && !NT_SUCCESS(Status))
|
||||
{
|
||||
|
@ -123,6 +146,8 @@ DriverUnload(
|
|||
|
||||
DPRINT("DriverUnload\n");
|
||||
|
||||
KmtCleanUsermodeCallbacks();
|
||||
|
||||
if (MainDeviceObject)
|
||||
{
|
||||
PKMT_DEVICE_EXTENSION DeviceExtension = MainDeviceObject->DeviceExtension;
|
||||
|
@ -403,6 +428,93 @@ DriverIoControl(
|
|||
ResultBuffer->LogBufferLength, ResultBuffer->LogBufferMaxLength);
|
||||
break;
|
||||
}
|
||||
case IOCTL_KMTEST_USERMODE_AWAIT_REQ:
|
||||
{
|
||||
PLIST_ENTRY Entry;
|
||||
PKMT_USER_WORK_ENTRY WorkItem;
|
||||
|
||||
DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_AWAIT_REQ, len=%lu\n",
|
||||
IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
|
||||
|
||||
/* TODO: prevent multiple concurrent invocations */
|
||||
Status = KeWaitForSingleObject(&WorkList.NewWorkEvent, UserRequest, UserMode, FALSE, NULL);
|
||||
if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC)
|
||||
break;
|
||||
|
||||
if (IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KMT_CALLBACK_REQUEST_PACKET))
|
||||
{
|
||||
Status = STATUS_INVALID_BUFFER_SIZE;
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT(!IsListEmpty(&WorkList.ListHead));
|
||||
|
||||
Entry = WorkList.ListHead.Flink;
|
||||
WorkItem = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
|
||||
|
||||
Length = sizeof(WorkItem->Request);
|
||||
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &WorkItem->Request, Length);
|
||||
Status = STATUS_SUCCESS;
|
||||
|
||||
KeResetEvent(&WorkList.NewWorkEvent);
|
||||
break;
|
||||
|
||||
}
|
||||
case IOCTL_KMTEST_USERMODE_SEND_RESPONSE:
|
||||
{
|
||||
PLIST_ENTRY Entry;
|
||||
PKMT_USER_WORK_ENTRY WorkEntry;
|
||||
PVOID Response;
|
||||
ULONG ResponseSize = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
|
||||
|
||||
DPRINT("DriverIoControl. IOCTL_KMTEST_USERMODE_SEND_RESPONSE, inlen=%lu, outlen=%lu\n",
|
||||
IoStackLocation->Parameters.DeviceIoControl.InputBufferLength,
|
||||
IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength);
|
||||
|
||||
if (IoStackLocation->Parameters.DeviceIoControl.InputBufferLength != sizeof(ULONG) || ResponseSize != sizeof(KMT_RESPONSE))
|
||||
{
|
||||
Status = STATUS_INVALID_BUFFER_SIZE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* FIXME: don't misuse the output buffer as an input! */
|
||||
Response = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
|
||||
if (Response == NULL)
|
||||
{
|
||||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||||
break;
|
||||
}
|
||||
|
||||
ExAcquireFastMutex(&WorkList.Lock);
|
||||
|
||||
Status = STATUS_OBJECTID_NOT_FOUND;
|
||||
|
||||
Entry = WorkList.ListHead.Flink;
|
||||
while (Entry != &WorkList.ListHead)
|
||||
{
|
||||
WorkEntry = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
|
||||
if (WorkEntry->Request.RequestId == *(PULONG)Irp->AssociatedIrp.SystemBuffer)
|
||||
{
|
||||
WorkEntry->Response = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_RESPONSE), 'pseR');
|
||||
if (WorkEntry->Response == NULL)
|
||||
{
|
||||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||||
break;
|
||||
}
|
||||
|
||||
RtlCopyMemory(WorkEntry->Response, Response, ResponseSize);
|
||||
KeSetEvent(&WorkEntry->WorkDoneEvent, IO_NO_INCREMENT, FALSE);
|
||||
Status = STATUS_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
Entry = Entry->Flink;
|
||||
}
|
||||
|
||||
ExReleaseFastMutex(&WorkList.Lock);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DPRINT1("DriverIoControl. Invalid IoCtl code 0x%08X\n",
|
||||
IoStackLocation->Parameters.DeviceIoControl.IoControlCode);
|
||||
|
@ -417,3 +529,113 @@ DriverIoControl(
|
|||
|
||||
return Status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name KmtUserModeCallback
|
||||
*
|
||||
* Enqueue a request to the usermode callback queue and blocks until the work
|
||||
* is finished.
|
||||
*
|
||||
* @param Operation
|
||||
* TODO
|
||||
* @param Parameters
|
||||
* TODO
|
||||
* TODO: why is this PVOID?
|
||||
*
|
||||
* @return Response from user mode
|
||||
*/
|
||||
PKMT_RESPONSE
|
||||
KmtUserModeCallback(
|
||||
IN KMT_CALLBACK_INFORMATION_CLASS Operation,
|
||||
IN PVOID Parameters)
|
||||
{
|
||||
PKMT_RESPONSE Result;
|
||||
NTSTATUS Status;
|
||||
PKMT_USER_WORK_ENTRY WorkEntry;
|
||||
LARGE_INTEGER Timeout;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
WorkEntry = ExAllocatePoolWithTag(PagedPool, sizeof(KMT_USER_WORK_ENTRY), 'ekrW');
|
||||
if (WorkEntry == NULL)
|
||||
return NULL;
|
||||
|
||||
KeInitializeEvent(&WorkEntry->WorkDoneEvent, NotificationEvent, FALSE);
|
||||
WorkEntry->Request.RequestId = RequestId++;
|
||||
WorkEntry->Request.OperationClass = Operation;
|
||||
WorkEntry->Request.Parameters = Parameters;
|
||||
WorkEntry->Response = NULL;
|
||||
|
||||
ExAcquireFastMutex(&WorkList.Lock);
|
||||
InsertTailList(&WorkList.ListHead, &WorkEntry->ListEntry);
|
||||
ExReleaseFastMutex(&WorkList.Lock);
|
||||
|
||||
KeSetEvent(&WorkList.NewWorkEvent, IO_NO_INCREMENT, FALSE);
|
||||
|
||||
Timeout.QuadPart = -10 * 1000 * 1000 * 10; //wait for 10 seconds
|
||||
Status = KeWaitForSingleObject(&WorkEntry->WorkDoneEvent, Executive, UserMode, FALSE, &Timeout);
|
||||
|
||||
if (Status == STATUS_USER_APC || Status == STATUS_KERNEL_APC || Status == STATUS_TIMEOUT)
|
||||
{
|
||||
DPRINT1("Unexpected callback abortion! Reason: %lx\n", Status);
|
||||
}
|
||||
|
||||
ExAcquireFastMutex(&WorkList.Lock);
|
||||
RemoveEntryList(&WorkEntry->ListEntry);
|
||||
ExReleaseFastMutex(&WorkList.Lock);
|
||||
|
||||
Result = WorkEntry->Response;
|
||||
|
||||
ExFreePoolWithTag(WorkEntry, 'ekrW');
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name KmtFreeCallbackResponse
|
||||
*
|
||||
* TODO
|
||||
*
|
||||
* @param Response
|
||||
* TODO
|
||||
*/
|
||||
VOID
|
||||
KmtFreeCallbackResponse(
|
||||
PKMT_RESPONSE Response)
|
||||
{
|
||||
PAGED_CODE();
|
||||
|
||||
ExFreePoolWithTag(Response, 'pseR');
|
||||
}
|
||||
|
||||
/**
|
||||
* @name KmtCleanUsermodeCallbacks
|
||||
*
|
||||
* TODO
|
||||
*/
|
||||
static
|
||||
VOID
|
||||
KmtCleanUsermodeCallbacks(VOID)
|
||||
{
|
||||
PLIST_ENTRY Entry;
|
||||
|
||||
PAGED_CODE();
|
||||
|
||||
ExAcquireFastMutex(&WorkList.Lock);
|
||||
|
||||
Entry = WorkList.ListHead.Flink;
|
||||
while (Entry != &WorkList.ListHead)
|
||||
{
|
||||
PKMT_USER_WORK_ENTRY WorkEntry = CONTAINING_RECORD(Entry, KMT_USER_WORK_ENTRY, ListEntry);
|
||||
if (WorkEntry->Response != NULL)
|
||||
{
|
||||
KmtFreeCallbackResponse(WorkEntry->Response);
|
||||
}
|
||||
|
||||
Entry = Entry->Flink;
|
||||
|
||||
ExFreePoolWithTag(WorkEntry, 'ekrW');
|
||||
}
|
||||
|
||||
ExReleaseFastMutex(&WorkList.Lock);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue