diff --git a/inc/usersim/ex.h b/inc/usersim/ex.h index c0fee2b..a78d1a5 100644 --- a/inc/usersim/ex.h +++ b/inc/usersim/ex.h @@ -184,6 +184,77 @@ extern "C" USERSIM_API void ExRaiseDatatypeMisalignment(); + /** + * @brief Allocate memory. + * @param[in] pool_type Pool type to use. + * @param[in] size Size of memory to allocate. + * @param[in] tag Pool tag to use. + * @param[in] initialize False to return "uninitialized" memory. + * @returns Pointer to memory block allocated, or null on failure. + */ + __drv_allocatesMem(Mem) _Must_inspect_result_ _Ret_writes_maybenull_(size) void* usersim_allocate_with_tag( + _In_ __drv_strictTypeMatch(__drv_typeExpr) POOL_TYPE pool_type, size_t size, uint32_t tag, bool initialize); + + /** + * @brief Allocate memory. + * @param[in] size Size of memory to allocate. + * @returns Pointer to memory block allocated, or null on failure. + */ + __drv_allocatesMem(Mem) _Must_inspect_result_ _Ret_writes_maybenull_(size) void* usersim_allocate(size_t size); + + /** + * @brief Reallocate memory. + * @param[in] memory Allocation to be reallocated. + * @param[in] old_size Old size of memory to reallocate. + * @param[in] new_size New size of memory to reallocate. + * @returns Pointer to memory block allocated, or null on failure. + */ + __drv_allocatesMem(Mem) _Must_inspect_result_ _Ret_writes_maybenull_(new_size) void* usersim_reallocate( + _In_ _Post_invalid_ void* memory, size_t old_size, size_t new_size); + + /** + * @brief Reallocate memory with tag. + * @param[in] memory Allocation to be reallocated. + * @param[in] old_size Old size of memory to reallocate. + * @param[in] new_size New size of memory to reallocate. + * @param[in] tag Pool tag to use. + * @returns Pointer to memory block allocated, or null on failure. + */ + __drv_allocatesMem(Mem) _Must_inspect_result_ _Ret_writes_maybenull_(new_size) void* usersim_reallocate_with_tag( + _In_ _Post_invalid_ void* memory, size_t old_size, size_t new_size, uint32_t tag); + + /** + * @brief Free memory. + * @param[in] memory Allocation to be freed. + */ + void + usersim_free(_Frees_ptr_opt_ void* memory); + + /** + * @brief Allocate memory that has a starting address that is cache aligned. + * @param[in] size Size of memory to allocate + * @returns Pointer to memory block allocated, or null on failure. + */ + USERSIM_API + __drv_allocatesMem(Mem) _Must_inspect_result_ + _Ret_writes_maybenull_(size) void* usersim_allocate_cache_aligned(size_t size); + + /** + * @brief Allocate memory that has a starting address that is cache aligned with tag. + * @param[in] size Size of memory to allocate + * @param[in] tag Pool tag to use. + * @returns Pointer to memory block allocated, or null on failure. + */ + __drv_allocatesMem(Mem) _Must_inspect_result_ + _Ret_writes_maybenull_(size) void* usersim_allocate_cache_aligned_with_tag(size_t size, uint32_t tag); + + /** + * @brief Free memory that has a starting address that is cache aligned. + * @param[in] memory Allocation to be freed. + */ + void + usersim_free_cache_aligned(_Frees_ptr_opt_ void* memory); + #if defined(__cplusplus) } @@ -207,4 +278,26 @@ ExRaiseAccessViolationCPP(); USERSIM_API void ExRaiseDatatypeMisalignmentCPP(); +void usersim_initialize_ex(bool leak_detector); +void usersim_clean_up_ex(); + +#ifdef __cplusplus +#include +namespace usersim_helper { + +struct _usersim_free_functor +{ + void + operator()(void* memory) + { + usersim_free(memory); + } +}; + +typedef std::unique_ptr usersim_memory_ptr; + +} // namespace usersim_helper + +#endif + #endif diff --git a/inc/usersim/fwp_test.h b/inc/usersim/fwp_test.h index 02b87f6..b062609 100644 --- a/inc/usersim/fwp_test.h +++ b/inc/usersim/fwp_test.h @@ -7,6 +7,11 @@ #include #include +#if defined(__cplusplus) +extern "C" +{ +#endif + typedef struct _fwp_classify_parameters { ADDRESS_FAMILY family; @@ -51,4 +56,8 @@ usersim_fwp_sock_ops_v6(_In_ fwp_classify_parameters_t* parameters); USERSIM_API void usersim_fwp_set_sublayer_guids( - _In_ const GUID& default_sublayer, _In_ const GUID& connect_v4_sublayer, _In_ const GUID& connect_v6_sublayer); \ No newline at end of file + _In_ const GUID& default_sublayer, _In_ const GUID& connect_v4_sublayer, _In_ const GUID& connect_v6_sublayer); + +#if defined(__cplusplus) +} +#endif diff --git a/inc/usersim/io.h b/inc/usersim/io.h index 1c403f1..3b8fa0e 100644 --- a/inc/usersim/io.h +++ b/inc/usersim/io.h @@ -25,7 +25,7 @@ extern "C" typedef struct _DEVICE_OBJECT DEVICE_OBJECT; - typedef struct _DRIVER_OBJECT DRIVER_OBJECT; + typedef struct _DRIVER_OBJECT DRIVER_OBJECT, *PDRIVER_OBJECT; typedef struct _IO_WORKITEM IO_WORKITEM, *PIO_WORKITEM; @@ -68,7 +68,13 @@ extern "C" IoGetFileObjectGenericMapping(); USERSIM_API - _IRQL_requires_max_(DISPATCH_LEVEL) NTKERNELAPI PEPROCESS IoGetCurrentProcess(VOID); + _IRQL_requires_max_(DISPATCH_LEVEL) PEPROCESS IoGetCurrentProcess(VOID); + + typedef struct _IRP* PIRP; + + USERSIM_API + _IRQL_requires_max_(DISPATCH_LEVEL) VOID + IofCompleteRequest(_In_ PIRP irp, _In_ CCHAR priority_boost); #if defined(__cplusplus) } diff --git a/inc/usersim/rtl.h b/inc/usersim/rtl.h index 3ac8f67..b59e3b3 100644 --- a/inc/usersim/rtl.h +++ b/inc/usersim/rtl.h @@ -145,14 +145,16 @@ extern "C" _Out_ PSTRING destination_string, _In_opt_ __drv_aliasesMem PCSTR source_string); - _IRQL_requires_max_(DISPATCH_LEVEL) _At_(DestinationString->Buffer, _Post_equal_to_(SourceString)) - _At_(DestinationString->Length, _Post_equal_to_(_String_length_(SourceString) * sizeof(WCHAR))) - _At_(DestinationString->MaximumLength, _Post_equal_to_((_String_length_(SourceString) + 1) * sizeof(WCHAR))) + _IRQL_requires_max_(DISPATCH_LEVEL) _At_(destination_string->Buffer, _Post_equal_to_(source_string)) + _At_(destination_string->Length, _Post_equal_to_(_String_length_(source_string) * sizeof(WCHAR))) + _At_(destination_string->MaximumLength, _Post_equal_to_((_String_length_(source_string) + 1) * sizeof(WCHAR))) USERSIM_API VOID NTAPI RtlInitUnicodeString( - _Out_ PCUNICODE_STRING destination_string, + _Out_ PUNICODE_STRING destination_string, _In_opt_z_ __drv_aliasesMem PCWSTR source_string); + typedef struct _object_attributes OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; + // Include Rtl* implementations from ntdll.lib. #pragma comment(lib, "ntdll.lib") diff --git a/inc/usersim/wdf.h b/inc/usersim/wdf.h index 76f504a..dd6744d 100644 --- a/inc/usersim/wdf.h +++ b/inc/usersim/wdf.h @@ -5,6 +5,7 @@ // implemented in usersim.dll, and can be used by unit tests. #pragma once #include "usersim/common.h" +#include "usersim/io.h" // For IRP #include "usersim/rtl.h" // For UNICODE_STRING #if defined(__cplusplus) @@ -13,18 +14,17 @@ extern "C" #endif typedef HANDLE WDFDEVICE; - typedef struct _wdfdriver WDFDRIVER; + typedef HANDLE WDFDRIVER; - typedef struct _WDF_OBJECT_ATTRIBUTES WDF_OBJECT_ATTRIBUTES, *PWDF_OBJECT_ATTRIBUTES; + typedef struct _WDF_OBJECT_ATTRIBUTES + { + int SynchronizationScope; + } WDF_OBJECT_ATTRIBUTES, *PWDF_OBJECT_ATTRIBUTES; typedef struct _WDFDEVICE_INIT WDFDEVICE_INIT, *PWDFDEVICE_INIT; #define WDF_NO_OBJECT_ATTRIBUTES 0 #define WDF_NO_HANDLE 0 - typedef struct _wdfdriver WDFDRIVER; - - typedef struct _driver_object DRIVER_OBJECT, *PDRIVER_OBJECT; - typedef NTSTATUS(DRIVER_INITIALIZE)(_In_ PDRIVER_OBJECT driver_object, _In_ PUNICODE_STRING registry_path); typedef struct _WDFDEVICE_INIT WDFDEVICE_INIT, *PWDFDEVICE_INIT; @@ -44,10 +44,10 @@ extern "C" ULONG DriverPoolTag; } WDF_DRIVER_CONFIG, *PWDF_DRIVER_CONFIG; - typedef struct _wdfdriver + struct _DRIVER_OBJECT { WDF_DRIVER_CONFIG config; - } WDFDRIVER; + }; #define WDF_DRIVER_GLOBALS_NAME_LEN (32) @@ -78,6 +78,113 @@ extern "C" void WDF_DRIVER_CONFIG_INIT(_Out_ PWDF_DRIVER_CONFIG config, _In_opt_ PFN_WDF_DRIVER_DEVICE_ADD evt_driver_device_add); + typedef NTSTATUS(WdfDriverCreate_t)( + _In_ WDF_DRIVER_GLOBALS* driver_globals, + _In_ PDRIVER_OBJECT driver_object, + _In_ PCUNICODE_STRING registry_path, + _In_opt_ PWDF_OBJECT_ATTRIBUTES driver_attributes, + _In_ PWDF_DRIVER_CONFIG driver_config, + _Out_opt_ WDFDRIVER* driver); + + typedef NTSTATUS(WdfDeviceCreate_t)( + _In_ WDF_DRIVER_GLOBALS* driver_globals, + _Inout_ PWDFDEVICE_INIT* device_init, + _In_opt_ PWDF_OBJECT_ATTRIBUTES device_attributes, + _Out_ WDFDEVICE* device); + + typedef _IRQL_requires_max_(DISPATCH_LEVEL) + VOID(WdfDeviceInitFree_t)(_In_ PWDF_DRIVER_GLOBALS driver_globals, _In_ PWDFDEVICE_INIT device_init); + + typedef _Must_inspect_result_ _IRQL_requires_max_(PASSIVE_LEVEL) PWDFDEVICE_INIT(WdfControlDeviceInitAllocate_t)( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ WDFDRIVER driver, + _In_ CONST UNICODE_STRING* sddl_string); + + typedef _IRQL_requires_max_(DISPATCH_LEVEL) VOID(WdfDeviceInitSetDeviceType_t)( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ PWDFDEVICE_INIT device_init, + DEVICE_TYPE device_type); + +#define FILE_AUTOGENERATED_DEVICE_NAME 0x00000080 +#define FILE_DEVICE_SECURE_OPEN 0x00000100 + + typedef _IRQL_requires_max_(DISPATCH_LEVEL) VOID(WdfDeviceInitSetCharacteristics_t)( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ PWDFDEVICE_INIT device_init, + ULONG device_characteristics, + BOOLEAN or_in_values); + + typedef _Must_inspect_result_ _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS (WdfDeviceInitAssignName_t)( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ PWDFDEVICE_INIT device_init, + _In_opt_ PCUNICODE_STRING device_name); + + typedef struct _WDF_FILEOBJECT_CONFIG + { + int unused; + } WDF_FILEOBJECT_CONFIG, *PWDF_FILEOBJECT_CONFIG; + + typedef _IRQL_requires_max_(DISPATCH_LEVEL) VOID (WdfDeviceInitSetFileObjectConfig_t)( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ PWDFDEVICE_INIT device_init, + _In_ PWDF_FILEOBJECT_CONFIG file_object_config, + _In_opt_ PWDF_OBJECT_ATTRIBUTES file_object_attributes); + + + typedef NTSTATUS(FN_WDFDEVICE_WDM_IRP_PREPROCESS)(_In_ WDFDEVICE device, _Inout_ IRP* irp); + typedef FN_WDFDEVICE_WDM_IRP_PREPROCESS* PFN_WDFDEVICE_WDM_IRP_PREPROCESS; + +#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a + + typedef _Must_inspect_result_ _IRQL_requires_max_(DISPATCH_LEVEL) NTSTATUS (WdfDeviceInitAssignWdmIrpPreprocessCallback_t)( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ PWDFDEVICE_INIT device_init, + _In_ PFN_WDFDEVICE_WDM_IRP_PREPROCESS evt_device_wdm_irp_preprocess, + UCHAR major_function, + _When_(num_minor_functions > 0, _In_reads_bytes_(num_minor_functions)) + _When_(num_minor_functions == 0, _In_opt_) PUCHAR minor_functions, + ULONG num_minor_functions); + + typedef _Must_inspect_result_ _IRQL_requires_max_(PASSIVE_LEVEL) NTSTATUS (WdfDeviceCreateSymbolicLink_t)( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ WDFDEVICE device, + _In_ PCUNICODE_STRING symbolic_link_name); + + typedef struct _WDF_IO_QUEUE_CONFIG + { + int unused; + } WDF_IO_QUEUE_CONFIG, *PWDF_IO_QUEUE_CONFIG; + typedef HANDLE WDFQUEUE; + + typedef _Must_inspect_result_ _IRQL_requires_max_(DISPATCH_LEVEL) NTSTATUS (WdfIoQueueCreate_t)( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ WDFDEVICE device, + _In_ PWDF_IO_QUEUE_CONFIG config, + _In_opt_ PWDF_OBJECT_ATTRIBUTES queue_attributes, + _Out_opt_ WDFQUEUE* queue); + + typedef HANDLE WDFOBJECT; + + typedef _IRQL_requires_max_(DISPATCH_LEVEL) + VOID(WdfObjectDelete_t)(_In_ PWDF_DRIVER_GLOBALS driver_globals, _In_ WDFOBJECT object); + + typedef enum _WDFFUNCENUM + { + WdfControlDeviceInitAllocateTableIndex = 25, + WdfDeviceInitFreeTableIndex = 54, + WdfDeviceInitSetDeviceTypeTableIndex = 66, + WdfDeviceInitAssignNameTableIndex = 67, + WdfDeviceInitSetCharacteristicsTableIndex = 70, + WdfDeviceInitSetFileObjectConfigTableIndex = 71, + WdfDeviceInitAssignWdmIrpPreprocessCallbackTableIndex = 73, + WdfDeviceCreateTableIndex = 75, + WdfDeviceCreateSymbolicLinkTableIndex = 80, + WdfDriverCreateTableIndex = 116, + WdfIoQueueCreateTableIndex = 152, + WdfObjectDeleteTableIndex = 208, + WdfFunctionTableNumEntries = 444, + } WDFFUNCENUM; + void usersim_initialize_wdf(); diff --git a/src/etw.cpp b/src/etw.cpp index 3a7ad1b..134e2b0 100644 --- a/src/etw.cpp +++ b/src/etw.cpp @@ -3,6 +3,7 @@ #include "platform.h" #include "usersim/etw.h" +#include "usersim/ex.h" typedef struct { diff --git a/src/ex.cpp b/src/ex.cpp index b0446be..3c95244 100644 --- a/src/ex.cpp +++ b/src/ex.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT #include "fault_injection.h" +#include "leak_detector.h" #include "platform.h" #include "kernel_um.h" #include "usersim/ex.h" @@ -15,6 +16,9 @@ // Ex* functions. +extern "C" size_t usersim_fuzzing_memory_limit = MAXSIZE_T; +usersim_leak_detector_ptr _usersim_leak_detector_ptr; + /*** * @brief This following class implements a mock of the Windows Kernel's rundown reference implementation. * 1) It uses a map to track the number of references to a given EX_RUNDOWN_REF structure. @@ -299,7 +303,7 @@ ExAllocatePoolUninitializedCPP( if (tag == 0) { KeBugCheckExCPP(BAD_POOL_CALLER, 0x9B, pool_type, number_of_bytes, 0); } - return usersim_allocate_with_tag(number_of_bytes, tag, false); + return usersim_allocate_with_tag(pool_type, number_of_bytes, tag, false); } _Ret_maybenull_ void* @@ -318,7 +322,171 @@ ExAllocatePoolWithTagCPP( if (tag == 0) { KeBugCheckExCPP(BAD_POOL_CALLER, 0x9B, pool_type, number_of_bytes, 0); } - return usersim_allocate_with_tag(number_of_bytes, tag, true); + return usersim_allocate_with_tag(pool_type, number_of_bytes, tag, true); +} + +#define USERSIM_CACHE_LINE_SIZE 64 + +typedef struct +{ + POOL_TYPE pool_type; + uint32_t tag; +} usersim_allocation_header_t; + +__drv_allocatesMem(Mem) _Must_inspect_result_ _Ret_writes_maybenull_(size) void* +usersim_allocate_with_tag( + _In_ __drv_strictTypeMatch(__drv_typeExpr) POOL_TYPE pool_type, size_t size, uint32_t tag, bool initialize) +{ + if (size == 0) { + KeBugCheckEx(BAD_POOL_CALLER, 0x00, 0, 0, 0); + } + if (size > usersim_fuzzing_memory_limit) { + return nullptr; + } + + if (usersim_fault_injection_inject_fault()) { + return nullptr; + } + + // Allocate space with a usersim_allocation_header_t prepended. + void* memory; + if (pool_type == NonPagedPoolNxCacheAligned) { + // The pointer we return has to be cache aligned so we allocate + // enough extra space to fill a cache line, and put the + // usersim_allocation_header_t at the end of that space. + // TODO: move logic into usersim_allocate_cache_aligned_with_tag(). + size_t full_size = USERSIM_CACHE_LINE_SIZE + size; + uint8_t* pointer = (uint8_t*)_aligned_malloc(full_size, USERSIM_CACHE_LINE_SIZE); + if (pointer == nullptr) { + return nullptr; + } + memory = pointer + USERSIM_CACHE_LINE_SIZE; + } else { + size_t full_size = sizeof(usersim_allocation_header_t) + size; + uint8_t* pointer = (uint8_t*)calloc(full_size, 1); + if (pointer == nullptr) { + return nullptr; + } + memory = pointer + sizeof(usersim_allocation_header_t); + } + + // Do any initialization. + auto header = (usersim_allocation_header_t*)((uint8_t*)memory - sizeof(usersim_allocation_header_t)); + header->pool_type = pool_type; + header->tag = tag; + if (!initialize) { + // The calloc call always zero-initializes memory. To test + // returning uninitialized memory, we explicitly fill it with 0xcc. + memset(memory, 0xcc, size); + } + + if (memory && _usersim_leak_detector_ptr) { + _usersim_leak_detector_ptr->register_allocation(reinterpret_cast(memory), size); + } + + return memory; +} + +__drv_allocatesMem(Mem) _Must_inspect_result_ _Ret_writes_maybenull_(new_size) void* +usersim_reallocate( + _In_ _Post_invalid_ void* memory, size_t old_size, size_t new_size) +{ + return usersim_reallocate_with_tag(memory, old_size, new_size, 0); +} + +__drv_allocatesMem(Mem) _Must_inspect_result_ _Ret_writes_maybenull_(new_size) void* +usersim_reallocate_with_tag( + _In_ _Post_invalid_ void* memory, size_t old_size, size_t new_size, uint32_t tag) +{ + UNREFERENCED_PARAMETER(tag); + UNREFERENCED_PARAMETER(old_size); + + if (new_size > usersim_fuzzing_memory_limit) { + return nullptr; + } + + if (usersim_fault_injection_inject_fault()) { + return nullptr; + } + + void* p; + auto header = (usersim_allocation_header_t*)((uint8_t*)memory - sizeof(usersim_allocation_header_t)); + if (header->pool_type == NonPagedPoolNxCacheAligned) { + uint8_t* pointer = ((uint8_t*)memory) - USERSIM_CACHE_LINE_SIZE; + p = _aligned_realloc(pointer, USERSIM_CACHE_LINE_SIZE + new_size, USERSIM_CACHE_LINE_SIZE); + } else { + uint8_t* pointer = ((uint8_t*)memory) - sizeof(usersim_allocation_header_t); + p = realloc(pointer, sizeof(usersim_allocation_header_t) + new_size); + } + + if (p && (new_size > old_size)) { + memset(((char*)p) + old_size, 0, new_size - old_size); + } + + if (_usersim_leak_detector_ptr) { + _usersim_leak_detector_ptr->unregister_allocation(reinterpret_cast(memory)); + _usersim_leak_detector_ptr->register_allocation(reinterpret_cast(p), new_size); + } + + return p; +} + +__drv_allocatesMem(Mem) _Must_inspect_result_ _Ret_writes_maybenull_(size) void* usersim_allocate(size_t size) +{ + return usersim_allocate_with_tag(NonPagedPool, size, 'tset', true); +} + +void +usersim_free(_Frees_ptr_opt_ void* memory) +{ + if (memory == nullptr) { + return; + } + auto header = (usersim_allocation_header_t*)((uint8_t*)memory - sizeof(usersim_allocation_header_t)); + if (_usersim_leak_detector_ptr) { + _usersim_leak_detector_ptr->unregister_allocation(reinterpret_cast(memory)); + } + if (header->pool_type == NonPagedPoolNxCacheAligned) { + uint8_t* pointer = ((uint8_t*)memory) - USERSIM_CACHE_LINE_SIZE; + _aligned_free(pointer); + } else { + uint8_t* pointer = ((uint8_t*)memory) - sizeof(usersim_allocation_header_t); + free(pointer); + } +} + +__drv_allocatesMem(Mem) _Must_inspect_result_ +_Ret_writes_maybenull_(size) void* +usersim_allocate_cache_aligned(size_t size) +{ + if (size > usersim_fuzzing_memory_limit) { + return nullptr; + } + + if (usersim_fault_injection_inject_fault()) { + return nullptr; + } + + void* memory = _aligned_malloc(size, USERSIM_CACHE_LINE_SIZE); + if (memory) { + memset(memory, 0, size); + } + return memory; +} + +__drv_allocatesMem(Mem) _Must_inspect_result_ +_Ret_writes_maybenull_(size) void* +usersim_allocate_cache_aligned_with_tag(size_t size, uint32_t tag) +{ + UNREFERENCED_PARAMETER(tag); + + return usersim_allocate_cache_aligned(size); +} + +void +usersim_free_cache_aligned(_Frees_ptr_opt_ void* memory) +{ + _aligned_free(memory); } _Ret_maybenull_ void* @@ -409,4 +577,21 @@ void ExRaiseDatatypeMisalignment() { ExRaiseDatatypeMisalignmentCPP(); +} + +void +usersim_initialize_ex(bool leak_detector) +{ + if (leak_detector) { + _usersim_leak_detector_ptr = std::make_unique(); + } +} + +void +usersim_clean_up_ex() +{ + if (_usersim_leak_detector_ptr) { + _usersim_leak_detector_ptr->dump_leaks(); + _usersim_leak_detector_ptr.reset(); + } } \ No newline at end of file diff --git a/src/io.cpp b/src/io.cpp index 914f5df..9d9a8ee 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -3,6 +3,7 @@ #include "platform.h" #include "kernel_um.h" +#include "usersim/ex.h" #include "usersim/io.h" // Io* functions. @@ -117,3 +118,10 @@ _IRQL_requires_max_(DISPATCH_LEVEL) NTKERNELAPI PEPROCESS IoGetCurrentProcess(VO { return (PEPROCESS)GetCurrentProcess(); } + +_IRQL_requires_max_(DISPATCH_LEVEL) VOID +IofCompleteRequest(_In_ PIRP irp, _In_ CCHAR priority_boost) +{ + UNREFERENCED_PARAMETER(irp); + UNREFERENCED_PARAMETER(priority_boost); +} \ No newline at end of file diff --git a/src/ke.cpp b/src/ke.cpp index 0767864..51d456d 100644 --- a/src/ke.cpp +++ b/src/ke.cpp @@ -266,7 +266,7 @@ usersim_free_semaphores() for (auto handle : *g_usersim_semaphore_handles) { ::CloseHandle(handle); } - usersim_free(g_usersim_semaphore_handles); + delete g_usersim_semaphore_handles; g_usersim_semaphore_handles = nullptr; } } @@ -731,7 +731,7 @@ usersim_free_threadpool_timers() for (TP_TIMER* threadpool_timer : *g_usersim_threadpool_timers) { CloseThreadpoolTimer(threadpool_timer); } - usersim_free(g_usersim_threadpool_timers); + delete g_usersim_threadpool_timers; g_usersim_threadpool_timers = nullptr; } } diff --git a/src/kernel_um.h b/src/kernel_um.h index d2130c7..b87e2e8 100644 --- a/src/kernel_um.h +++ b/src/kernel_um.h @@ -42,6 +42,7 @@ extern "C" #define STATUS_OBJECT_NAME_EXISTS ((NTSTATUS)0x40000000L) #define RPC_NT_CALL_FAILED ((NTSTATUS)0xC002001BL) #define STATUS_INVALID_IMAGE_FORMAT ((NTSTATUS)0xC000007BL) +#define STATUS_DRIVER_INTERNAL_ERROR ((NTSTATUS)0xC0000183L) #define STATUS_TOO_MANY_NODES ((NTSTATUS)0xC000020EL) #define STATUS_GENERIC_COMMAND_FAILED ((NTSTATUS)0xC0150026L) #define STATUS_CONTENT_BLOCKED ((NTSTATUS)0xC0000804L) diff --git a/src/leak_detector.cpp b/src/leak_detector.cpp index 8416088..62b827b 100644 --- a/src/leak_detector.cpp +++ b/src/leak_detector.cpp @@ -3,6 +3,7 @@ #include "leak_detector.h" #include "symbol_decoder.h" +#include "usersim/ke.h" #include #include @@ -30,6 +31,9 @@ void _usersim_leak_detector::unregister_allocation(uintptr_t address) { std::unique_lock lock(_mutex); + if (!_allocations.contains(address)) { + KeBugCheck(0); + } _allocations.erase(address); } diff --git a/src/ndis.h b/src/ndis.h index c27e1aa..2d4f4fd 100644 --- a/src/ndis.h +++ b/src/ndis.h @@ -9,6 +9,11 @@ #include #include +#if defined(__cplusplus) +extern "C" +{ +#endif + #define NET_BUFFER_FIRST_MDL(_NB) ((_NB)->MdlChain) #define NDIS_STATUS_SUCCESS ((NDIS_STATUS)STATUS_SUCCESS) #define NET_BUFFER_LIST_FIRST_NB(_NBL) ((_NBL)->FirstNetBuffer) @@ -43,41 +48,41 @@ typedef struct _NET_BUFFER_LIST_CONTEXT NET_BUFFER_LIST_CONTEXT, *PNET_BUFFER_LI typedef struct _NDIS_GENERIC_OBJECT NDIS_GENERIC_OBJECT, *PNDIS_GENERIC_OBJECT; -__declspec(dllexport) PNDIS_GENERIC_OBJECT +USERSIM_API PNDIS_GENERIC_OBJECT NdisAllocateGenericObject(_In_opt_ DRIVER_OBJECT* driver_object, _In_ unsigned long tag, _In_ uint16_t size); -__declspec(dllexport) NDIS_HANDLE +USERSIM_API NDIS_HANDLE NdisAllocateNetBufferListPool(_In_opt_ NDIS_HANDLE ndis_handle, _In_ NET_BUFFER_LIST_POOL_PARAMETERS const* parameters); -__declspec(dllexport) NET_BUFFER_LIST* +USERSIM_API NET_BUFFER_LIST* NdisAllocateCloneNetBufferList( _In_ NET_BUFFER_LIST* original_net_buffer_list, _In_ NDIS_HANDLE net_buffer_list_pool_handle, _In_ NDIS_HANDLE net_buffer_pool_handle, ULONG allocate_clone_flags); -__declspec(dllexport) void +USERSIM_API void NdisFreeCloneNetBufferList(_In_ NET_BUFFER_LIST* clone_net_buffer_list, ULONG free_clone_flags); -__declspec(dllexport) PNET_BUFFER_LIST +USERSIM_API PNET_BUFFER_LIST NdisAllocateNetBufferList(_In_ NDIS_HANDLE nbl_pool_handle, _In_ USHORT context_size, _In_ USHORT context_backfill); -__declspec(dllexport) _Must_inspect_result_ __drv_allocatesMem(mem) NET_BUFFER* NdisAllocateNetBuffer( +USERSIM_API _Must_inspect_result_ __drv_allocatesMem(mem) NET_BUFFER* NdisAllocateNetBuffer( _In_ NDIS_HANDLE pool_handle, _In_opt_ MDL* mdl_chain, _In_ unsigned long data_offset, _In_ SIZE_T data_length); -__declspec(dllexport) VOID +USERSIM_API VOID NdisFreeNetBuffer(_In_ __drv_freesMem(mem) NET_BUFFER* net_buffer); -__declspec(dllexport) VOID +USERSIM_API VOID NdisFreeNetBufferList(_In_ __drv_freesMem(mem) NET_BUFFER_LIST* net_buffer_list); -__declspec(dllexport) void +USERSIM_API void NdisFreeNetBufferListPool(_In_ __drv_freesMem(mem) NDIS_HANDLE pool_handle); -__declspec(dllexport) void +USERSIM_API void NdisFreeGenericObject(_In_ PNDIS_GENERIC_OBJECT ndis_object); -__declspec(dllexport) void* +USERSIM_API void* NdisGetDataBuffer( _In_ NET_BUFFER* net_buffer, _In_ unsigned long bytes_needed, @@ -85,16 +90,20 @@ NdisGetDataBuffer( _In_ unsigned long align_multiple, _In_ unsigned long align_offset); -__declspec(dllexport) NDIS_STATUS +USERSIM_API NDIS_STATUS NdisRetreatNetBufferDataStart( _In_ NET_BUFFER* net_buffer, _In_ unsigned long data_offset_delta, _In_ unsigned long data_back_fill, _In_opt_ void* allocate_mdl_handler); -__declspec(dllexport) void +USERSIM_API void NdisAdvanceNetBufferDataStart( _In_ NET_BUFFER* net_buffer, _In_ unsigned long data_offset_delta, _In_ BOOLEAN free_mdl, _In_opt_ void* free_mdl_handler); + +#if defined(__cplusplus) +} +#endif \ No newline at end of file diff --git a/src/platform.h b/src/platform.h index 8cf1d10..e178fff 100644 --- a/src/platform.h +++ b/src/platform.h @@ -99,76 +99,6 @@ extern "C" void usersim_platform_terminate(); - /** - * @brief Allocate memory. - * @param[in] size Size of memory to allocate. - * @returns Pointer to memory block allocated, or null on failure. - */ - __drv_allocatesMem(Mem) _Must_inspect_result_ _Ret_writes_maybenull_(size) void* usersim_allocate(size_t size); - - /** - * @brief Allocate memory. - * @param[in] size Size of memory to allocate. - * @param[in] tag Pool tag to use. - * @param[in] initialize False to return "uninitialized" memory. - * @returns Pointer to memory block allocated, or null on failure. - */ - __drv_allocatesMem(Mem) _Must_inspect_result_ - _Ret_writes_maybenull_(size) void* usersim_allocate_with_tag(size_t size, uint32_t tag, bool initialize); - - /** - * @brief Reallocate memory. - * @param[in] memory Allocation to be reallocated. - * @param[in] old_size Old size of memory to reallocate. - * @param[in] new_size New size of memory to reallocate. - * @returns Pointer to memory block allocated, or null on failure. - */ - __drv_allocatesMem(Mem) _Must_inspect_result_ _Ret_writes_maybenull_(new_size) void* usersim_reallocate( - _In_ _Post_invalid_ void* memory, size_t old_size, size_t new_size); - - /** - * @brief Reallocate memory with tag. - * @param[in] memory Allocation to be reallocated. - * @param[in] old_size Old size of memory to reallocate. - * @param[in] new_size New size of memory to reallocate. - * @param[in] tag Pool tag to use. - * @returns Pointer to memory block allocated, or null on failure. - */ - __drv_allocatesMem(Mem) _Must_inspect_result_ _Ret_writes_maybenull_(new_size) void* usersim_reallocate_with_tag( - _In_ _Post_invalid_ void* memory, size_t old_size, size_t new_size, uint32_t tag); - - /** - * @brief Free memory. - * @param[in] memory Allocation to be freed. - */ - void - usersim_free(_Frees_ptr_opt_ void* memory); - - /** - * @brief Allocate memory that has a starting address that is cache aligned. - * @param[in] size Size of memory to allocate - * @returns Pointer to memory block allocated, or null on failure. - */ - USERSIM_API - __drv_allocatesMem(Mem) _Must_inspect_result_ - _Ret_writes_maybenull_(size) void* usersim_allocate_cache_aligned(size_t size); - - /** - * @brief Allocate memory that has a starting address that is cache aligned with tag. - * @param[in] size Size of memory to allocate - * @param[in] tag Pool tag to use. - * @returns Pointer to memory block allocated, or null on failure. - */ - __drv_allocatesMem(Mem) _Must_inspect_result_ - _Ret_writes_maybenull_(size) void* usersim_allocate_cache_aligned_with_tag(size_t size, uint32_t tag); - - /** - * @brief Free memory that has a starting address that is cache aligned. - * @param[in] memory Allocation to be freed. - */ - void - usersim_free_cache_aligned(_Frees_ptr_opt_ void* memory); - typedef enum _usersim_page_protection { USERSIM_PAGE_PROTECT_READ_ONLY, @@ -775,22 +705,3 @@ extern "C" #ifdef __cplusplus } #endif - -#ifdef __cplusplus -#include -namespace usersim_helper { - -struct _usersim_free_functor -{ - void - operator()(void* memory) - { - usersim_free(memory); - } -}; - -typedef std::unique_ptr usersim_memory_ptr; - -} // namespace usersim_helper - -#endif diff --git a/src/platform_user.cpp b/src/platform_user.cpp index 304ff2c..2fc092e 100644 --- a/src/platform_user.cpp +++ b/src/platform_user.cpp @@ -37,12 +37,9 @@ bool _usersim_platform_is_preemptible = true; int32_t _usersim_platform_initiate_count = 0; extern "C" bool usersim_fuzzing_enabled = false; -extern "C" size_t usersim_fuzzing_memory_limit = MAXSIZE_T; static EX_RUNDOWN_REF _usersim_platform_preemptible_work_items_rundown; -usersim_leak_detector_ptr _usersim_leak_detector_ptr; - /** * @brief Environment variable to enable fault injection testing. * @@ -204,9 +201,9 @@ usersim_platform_initiate() try { _usersim_platform_maximum_group_count = GetMaximumProcessorGroupCount(); _usersim_platform_maximum_processor_count = GetMaximumProcessorCount(ALL_PROCESSOR_GROUPS); - auto fault_injection_stack_depth = + size_t fault_injection_stack_depth = _get_environment_variable_as_size_t(USERSIM_FAULT_INJECTION_SIMULATION_ENVIRONMENT_VARIABLE_NAME); - auto leak_detector = _get_environment_variable_as_bool(USERSIM_MEMORY_LEAK_DETECTION_ENVIRONMENT_VARIABLE_NAME); + bool leak_detector = _get_environment_variable_as_bool(USERSIM_MEMORY_LEAK_DETECTION_ENVIRONMENT_VARIABLE_NAME); if (fault_injection_stack_depth || leak_detector) { _usersim_symbol_decoder_initialize(); } @@ -218,9 +215,7 @@ usersim_platform_initiate() usersim_fuzzing_enabled = true; } - if (leak_detector) { - _usersim_leak_detector_ptr = std::make_unique(); - } + usersim_initialize_ex(leak_detector); result = usersim_initialize_irql(); if (result != STATUS_SUCCESS) { @@ -269,10 +264,7 @@ usersim_platform_terminate() usersim_clean_up_dpcs(); usersim_clean_up_irql(); _clean_up_thread_pool(); - if (_usersim_leak_detector_ptr) { - _usersim_leak_detector_ptr->dump_leaks(); - _usersim_leak_detector_ptr.reset(); - } + usersim_clean_up_ex(); int32_t count = InterlockedDecrement((volatile long*)&_usersim_platform_initiate_count); if (count < 0) { @@ -294,114 +286,6 @@ usersim_get_code_integrity_state(_Out_ usersim_code_integrity_state_t* state) USERSIM_RETURN_RESULT(STATUS_SUCCESS); } -__drv_allocatesMem(Mem) _Must_inspect_result_ _Ret_writes_maybenull_(size) void* usersim_allocate(size_t size) -{ - return usersim_allocate_with_tag(size, 'tset', true); -} - -__drv_allocatesMem(Mem) _Must_inspect_result_ - _Ret_writes_maybenull_(size) void* usersim_allocate_with_tag(size_t size, uint32_t tag, bool initialize) -{ - UNREFERENCED_PARAMETER(tag); - if (size == 0) { - KeBugCheckEx(BAD_POOL_CALLER, 0x00, 0, 0, 0); - } - if (size > usersim_fuzzing_memory_limit) { - return nullptr; - } - - if (usersim_fault_injection_inject_fault()) { - return nullptr; - } - - void* memory = calloc(size, 1); - if (!initialize) { - // The calloc call always zero-initializes memory. To test - // returning uninitialized memory, we explicitly fill it with 0xcc. - memset(memory, 0xcc, size); - } - - if (memory && _usersim_leak_detector_ptr) { - _usersim_leak_detector_ptr->register_allocation(reinterpret_cast(memory), size); - } - - return memory; -} - -__drv_allocatesMem(Mem) _Must_inspect_result_ _Ret_writes_maybenull_(new_size) void* usersim_reallocate( - _In_ _Post_invalid_ void* memory, size_t old_size, size_t new_size) -{ - UNREFERENCED_PARAMETER(old_size); - if (new_size > usersim_fuzzing_memory_limit) { - return nullptr; - } - - if (usersim_fault_injection_inject_fault()) { - return nullptr; - } - - void* p = realloc(memory, new_size); - if (p && (new_size > old_size)) { - memset(((char*)p) + old_size, 0, new_size - old_size); - } - - if (_usersim_leak_detector_ptr) { - _usersim_leak_detector_ptr->unregister_allocation(reinterpret_cast(memory)); - _usersim_leak_detector_ptr->register_allocation(reinterpret_cast(p), new_size); - } - - return p; -} - -__drv_allocatesMem(Mem) _Must_inspect_result_ _Ret_writes_maybenull_(new_size) void* usersim_reallocate_with_tag( - _In_ _Post_invalid_ void* memory, size_t old_size, size_t new_size, uint32_t tag) -{ - UNREFERENCED_PARAMETER(tag); - - return usersim_reallocate(memory, old_size, new_size); -} - -void -usersim_free(_Frees_ptr_opt_ void* memory) -{ - if (_usersim_leak_detector_ptr) { - _usersim_leak_detector_ptr->unregister_allocation(reinterpret_cast(memory)); - } - free(memory); -} - -__drv_allocatesMem(Mem) _Must_inspect_result_ - _Ret_writes_maybenull_(size) void* usersim_allocate_cache_aligned(size_t size) -{ - if (size > usersim_fuzzing_memory_limit) { - return nullptr; - } - - if (usersim_fault_injection_inject_fault()) { - return nullptr; - } - - void* memory = _aligned_malloc(size, USERSIM_CACHE_LINE_SIZE); - if (memory) { - memset(memory, 0, size); - } - return memory; -} - -__drv_allocatesMem(Mem) _Must_inspect_result_ - _Ret_writes_maybenull_(size) void* usersim_allocate_cache_aligned_with_tag(size_t size, uint32_t tag) -{ - UNREFERENCED_PARAMETER(tag); - - return usersim_allocate_cache_aligned(size); -} - -void -usersim_free_cache_aligned(_Frees_ptr_opt_ void* memory) -{ - _aligned_free(memory); -} - struct _usersim_ring_descriptor { void* primary_view; diff --git a/src/se.cpp b/src/se.cpp index 59b7ec2..c4ada13 100644 --- a/src/se.cpp +++ b/src/se.cpp @@ -4,6 +4,7 @@ #include "fault_injection.h" #include "kernel_um.h" #include "platform.h" +#include "usersim/ex.h" #include "usersim/se.h" #include "utilities.h" #include diff --git a/src/wdf.cpp b/src/wdf.cpp index 8195a50..0d71b68 100644 --- a/src/wdf.cpp +++ b/src/wdf.cpp @@ -1,10 +1,26 @@ // Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MIT +#include "fault_injection.h" #include "framework.h" +#include "platform.h" +#include "usersim/ex.h" #include "usersim/wdf.h" -static WDFDRIVER g_CurrentDriver = {}; +WDF_DRIVER_GLOBALS g_UsersimWdfDriverGlobals = {0}; + +static WdfDriverCreate_t _WdfDriverCreate; +static WdfDeviceCreate_t _WdfDeviceCreate; +static WdfControlDeviceInitAllocate_t _WdfControlDeviceInitAllocate; +static WdfDeviceInitFree_t _WdfDeviceInitFree; +static WdfDeviceInitSetDeviceType_t _WdfDeviceInitSetDeviceType; +static WdfDeviceInitSetCharacteristics_t _WdfDeviceInitSetCharacteristics; +static WdfDeviceInitAssignName_t _WdfDeviceInitAssignName; +static WdfDeviceInitSetFileObjectConfig_t _WdfDeviceInitSetFileObjectConfig; +static WdfDeviceInitAssignWdmIrpPreprocessCallback_t _WdfDeviceInitAssignWdmIrpPreprocessCallback; +static WdfDeviceCreateSymbolicLink_t _WdfDeviceCreateSymbolicLink; +static WdfIoQueueCreate_t _WdfIoQueueCreate; +static WdfObjectDelete_t _WdfObjectDelete; static NTSTATUS _WdfDriverCreate( @@ -15,14 +31,19 @@ _WdfDriverCreate( _In_ PWDF_DRIVER_CONFIG driver_config, _Out_opt_ WDFDRIVER* driver) { - UNREFERENCED_PARAMETER(driver_globals); - UNREFERENCED_PARAMETER(driver_object); UNREFERENCED_PARAMETER(registry_path); UNREFERENCED_PARAMETER(driver_attributes); - g_CurrentDriver.config = *driver_config; + if (driver_globals != &g_UsersimWdfDriverGlobals) { + return STATUS_INVALID_PARAMETER; + } + if (driver_globals->Driver != nullptr) { + return STATUS_DRIVER_INTERNAL_ERROR; + } + g_UsersimWdfDriverGlobals.Driver = driver_object; + driver_object->config = *driver_config; if (driver != nullptr) { - *driver = g_CurrentDriver; + *driver = driver_object; } return STATUS_SUCCESS; } @@ -30,34 +51,224 @@ _WdfDriverCreate( static NTSTATUS _WdfDeviceCreate( _In_ WDF_DRIVER_GLOBALS* driver_globals, - _Inout_ PWDFDEVICE_INIT* device_init, _In_opt_ PWDF_OBJECT_ATTRIBUTES device_attributes, _Out_ WDFDEVICE* device) + _Inout_ PWDFDEVICE_INIT* device_init, + _In_opt_ PWDF_OBJECT_ATTRIBUTES device_attributes, + _Out_ WDFDEVICE* device) { - UNREFERENCED_PARAMETER(driver_globals); - UNREFERENCED_PARAMETER(device_init); UNREFERENCED_PARAMETER(device_attributes); - *device = nullptr; + if (driver_globals != &g_UsersimWdfDriverGlobals) { + return STATUS_INVALID_PARAMETER; + } + if (usersim_fault_injection_inject_fault()) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + *device = *device_init; + return STATUS_SUCCESS; +} + +#define IRP_MJ_MAXIMUM_FUNCTION 0x1b + +typedef struct _WDFDEVICE_INIT +{ + DEVICE_TYPE device_type; + ULONG device_characteristics; + UNICODE_STRING device_name; + PWDF_FILEOBJECT_CONFIG file_object_config; + PWDF_OBJECT_ATTRIBUTES file_object_attributes; + PFN_WDFDEVICE_WDM_IRP_PREPROCESS evt_device_wdm_irp_preprocess; + PUCHAR minor_functions[IRP_MJ_MAXIMUM_FUNCTION]; + ULONG num_minor_functions[IRP_MJ_MAXIMUM_FUNCTION]; +} WDFDEVICE_INIT; + +static +_Must_inspect_result_ +_IRQL_requires_max_(PASSIVE_LEVEL) +PWDFDEVICE_INIT +_WdfControlDeviceInitAllocate( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ WDFDRIVER driver, + _In_ CONST UNICODE_STRING* sddl_string) +{ + UNREFERENCED_PARAMETER(driver_globals); + UNREFERENCED_PARAMETER(driver); + UNREFERENCED_PARAMETER(sddl_string); + + PWDFDEVICE_INIT device_init = (PWDFDEVICE_INIT)usersim_allocate(sizeof(*device_init)); + return device_init; +} + +static _IRQL_requires_max_(DISPATCH_LEVEL) VOID +_WdfDeviceInitFree(_In_ PWDF_DRIVER_GLOBALS driver_globals, _In_ PWDFDEVICE_INIT device_init) +{ + UNREFERENCED_PARAMETER(driver_globals); + usersim_free(device_init); +} + +static +_IRQL_requires_max_(DISPATCH_LEVEL) VOID +_WdfDeviceInitSetDeviceType( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ PWDFDEVICE_INIT device_init, + DEVICE_TYPE device_type) +{ + UNREFERENCED_PARAMETER(driver_globals); + device_init->device_type = device_type; +} + +static +_IRQL_requires_max_(DISPATCH_LEVEL) VOID +_WdfDeviceInitSetCharacteristics( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ PWDFDEVICE_INIT device_init, + ULONG device_characteristics, + BOOLEAN or_in_values) +{ + UNREFERENCED_PARAMETER(driver_globals); + if (!or_in_values) { + device_init->device_characteristics = 0; + } + device_init->device_characteristics |= device_characteristics; +} + +static _Must_inspect_result_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +_WdfDeviceInitAssignName( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ PWDFDEVICE_INIT device_init, + _In_opt_ PCUNICODE_STRING device_name) +{ + if (driver_globals != &g_UsersimWdfDriverGlobals) { + return STATUS_INVALID_PARAMETER; + } + if (usersim_fault_injection_inject_fault()) { + return STATUS_INSUFFICIENT_RESOURCES; + } + if (device_name != nullptr) { + device_init->device_name = *device_name; + } + return STATUS_SUCCESS; +} + +static +_IRQL_requires_max_(DISPATCH_LEVEL) VOID +_WdfDeviceInitSetFileObjectConfig( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ PWDFDEVICE_INIT device_init, + _In_ PWDF_FILEOBJECT_CONFIG file_object_config, + _In_opt_ PWDF_OBJECT_ATTRIBUTES file_object_attributes) +{ + UNREFERENCED_PARAMETER(driver_globals); + device_init->file_object_config = file_object_config; + device_init->file_object_attributes = file_object_attributes; +} + +static _Must_inspect_result_ +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +_WdfDeviceInitAssignWdmIrpPreprocessCallback( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ PWDFDEVICE_INIT device_init, + _In_ PFN_WDFDEVICE_WDM_IRP_PREPROCESS evt_device_wdm_irp_preprocess, + UCHAR major_function, + _When_(num_minor_functions > 0, _In_reads_bytes_(num_minor_functions)) _When_(num_minor_functions == 0, _In_opt_) + PUCHAR minor_functions, + ULONG num_minor_functions) +{ + if (driver_globals != &g_UsersimWdfDriverGlobals) { + return STATUS_INVALID_PARAMETER; + } + if (device_init->minor_functions[major_function] != nullptr) { + return STATUS_INVALID_DEVICE_REQUEST; + } + if (major_function >= IRP_MJ_MAXIMUM_FUNCTION) { + return STATUS_INVALID_PARAMETER; + } + if (usersim_fault_injection_inject_fault()) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + device_init->evt_device_wdm_irp_preprocess = evt_device_wdm_irp_preprocess; + device_init->minor_functions[major_function] = minor_functions; + device_init->num_minor_functions[major_function] = num_minor_functions; return STATUS_SUCCESS; } -WDF_DRIVER_GLOBALS g_UsersimWdfDriverGlobals = { - .Driver = g_CurrentDriver, -}; +static _Must_inspect_result_ +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSTATUS +_WdfDeviceCreateSymbolicLink( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ WDFDEVICE device, + _In_ PCUNICODE_STRING symbolic_link_name) +{ + UNREFERENCED_PARAMETER(device); + UNREFERENCED_PARAMETER(symbolic_link_name); -typedef enum _WDFFUNCENUM + if (driver_globals != &g_UsersimWdfDriverGlobals) { + return STATUS_INVALID_PARAMETER; + } + if (usersim_fault_injection_inject_fault()) { + return STATUS_INSUFFICIENT_RESOURCES; + } + return STATUS_SUCCESS; +} + +static +_Must_inspect_result_ +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSTATUS +_WdfIoQueueCreate( + _In_ PWDF_DRIVER_GLOBALS driver_globals, + _In_ WDFDEVICE device, + _In_ PWDF_IO_QUEUE_CONFIG config, + _In_opt_ PWDF_OBJECT_ATTRIBUTES queue_attributes, + _Out_opt_ WDFQUEUE* queue) +{ + UNREFERENCED_PARAMETER(device); + UNREFERENCED_PARAMETER(config); + UNREFERENCED_PARAMETER(queue_attributes); + + if (driver_globals != &g_UsersimWdfDriverGlobals) { + return STATUS_INVALID_PARAMETER; + } + if (usersim_fault_injection_inject_fault()) { + return STATUS_INSUFFICIENT_RESOURCES; + } + if (queue != nullptr) { + memset(queue, 0, sizeof(*queue)); + } + return STATUS_SUCCESS; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) VOID +_WdfObjectDelete(_In_ PWDF_DRIVER_GLOBALS driver_globals, _In_ WDFOBJECT object) { - WdfDeviceCreateTableIndex = 75, - WdfDriverCreateTableIndex = 116, - WdfFunctionTableNumEntries = 444, -} WDFFUNCENUM; + UNREFERENCED_PARAMETER(driver_globals); + + usersim_free(object); +} WDFFUNC g_UsersimWdfFunctions[WdfFunctionTableNumEntries]; void usersim_initialize_wdf() { + g_UsersimWdfFunctions[WdfControlDeviceInitAllocateTableIndex] = (WDFFUNC)_WdfControlDeviceInitAllocate; + g_UsersimWdfFunctions[WdfDeviceInitFreeTableIndex] = (WDFFUNC)_WdfDeviceInitFree; + g_UsersimWdfFunctions[WdfDeviceInitSetDeviceTypeTableIndex] = (WDFFUNC)_WdfDeviceInitSetDeviceType; + g_UsersimWdfFunctions[WdfDeviceInitAssignNameTableIndex] = (WDFFUNC)_WdfDeviceInitAssignName; + g_UsersimWdfFunctions[WdfDeviceInitSetCharacteristicsTableIndex] = (WDFFUNC)_WdfDeviceInitSetCharacteristics; + g_UsersimWdfFunctions[WdfDeviceInitSetFileObjectConfigTableIndex] = (WDFFUNC)_WdfDeviceInitSetFileObjectConfig; + g_UsersimWdfFunctions[WdfDeviceInitAssignWdmIrpPreprocessCallbackTableIndex] = + (WDFFUNC)_WdfDeviceInitAssignWdmIrpPreprocessCallback; g_UsersimWdfFunctions[WdfDeviceCreateTableIndex] = (WDFFUNC)_WdfDeviceCreate; + g_UsersimWdfFunctions[WdfDeviceCreateSymbolicLinkTableIndex] = (WDFFUNC)_WdfDeviceCreateSymbolicLink; g_UsersimWdfFunctions[WdfDriverCreateTableIndex] = (WDFFUNC)_WdfDriverCreate; + g_UsersimWdfFunctions[WdfIoQueueCreateTableIndex] = (WDFFUNC)_WdfIoQueueCreate; + g_UsersimWdfFunctions[WdfObjectDeleteTableIndex] = (WDFFUNC)_WdfObjectDelete; } extern "C" @@ -69,7 +280,7 @@ extern "C" } WDFDRIVER -WdfGetDriver() { return g_CurrentDriver; } +WdfGetDriver() { return g_UsersimWdfDriverGlobals.Driver; } void WDF_DRIVER_CONFIG_INIT(_Out_ PWDF_DRIVER_CONFIG config, _In_opt_ PFN_WDF_DRIVER_DEVICE_ADD evt_driver_device_add) diff --git a/tests/ex_test.cpp b/tests/ex_test.cpp index 633f7a2..4de3bd9 100644 --- a/tests/ex_test.cpp +++ b/tests/ex_test.cpp @@ -10,12 +10,21 @@ TEST_CASE("ExAllocatePool", "[ex]") { + // Try an allocation that need not be cache aligned. uint64_t* buffer = (uint64_t*)ExAllocatePoolUninitialized(NonPagedPool, 8, 'tset'); REQUIRE(buffer != nullptr); REQUIRE(*buffer != 0); *buffer = 0; ExFreePool(buffer); + // Try an allocation that must be cache aligned. + buffer = (uint64_t*)ExAllocatePoolUninitialized(NonPagedPoolNxCacheAligned, 8, 'tset'); + REQUIRE(buffer != nullptr); + REQUIRE(*buffer != 0); + REQUIRE((((uintptr_t)buffer) % 64) == 0); + *buffer = 0; + ExFreePool(buffer); + buffer = (uint64_t*)ExAllocatePoolWithTag(NonPagedPool, 8, 'tset'); REQUIRE(buffer != nullptr); REQUIRE(*buffer == 0); diff --git a/tests/wdf_test.cpp b/tests/wdf_test.cpp index f93869e..860d61d 100644 --- a/tests/wdf_test.cpp +++ b/tests/wdf_test.cpp @@ -7,11 +7,129 @@ #else #include #endif +#include "usersim/wdf.h" TEST_CASE("DriverEntry", "[wdf]") { HMODULE module = LoadLibraryW(L"sample.dll"); REQUIRE(module != nullptr); - FreeLibrary(module); + if (module) { + FreeLibrary(module); + } } + +extern "C" +{ + __declspec(dllimport) const WDFFUNC* UsersimWdfFunctions; + __declspec(dllimport) PWDF_DRIVER_GLOBALS UsersimWdfDriverGlobals; +} + +TEST_CASE("WdfDriverCreate", "[wdf]") +{ + DRIVER_OBJECT driver_object = {0}; + WDF_DRIVER_CONFIG config; + WDF_DRIVER_CONFIG_INIT(&config, nullptr); + + WdfDriverCreate_t* WdfDriverCreate = (WdfDriverCreate_t*)UsersimWdfFunctions[WdfDriverCreateTableIndex]; + WDFDRIVER driver = nullptr; + DECLARE_CONST_UNICODE_STRING(registry_path, L""); + NTSTATUS status = + WdfDriverCreate(UsersimWdfDriverGlobals, &driver_object, ®istry_path, nullptr, &config, &driver); + REQUIRE(status == STATUS_SUCCESS); + REQUIRE(driver != nullptr); + + // Verify that a duplicate create fails. + status = WdfDriverCreate(UsersimWdfDriverGlobals, &driver_object, ®istry_path, nullptr, &config, &driver); + REQUIRE(status == STATUS_DRIVER_INTERNAL_ERROR); + + UsersimWdfDriverGlobals->Driver = nullptr; +} + +NTSTATUS +driver_query_volume_information(_In_ WDFDEVICE device, _Inout_ IRP* irp) +{ + UNREFERENCED_PARAMETER(device); + UNREFERENCED_PARAMETER(irp); + return STATUS_SUCCESS; +} + +TEST_CASE("WdfDeviceCreate", "[wdf]") +{ + // Create driver to hold device. + DRIVER_OBJECT driver_object = {0}; + WDF_DRIVER_CONFIG config; + WDF_DRIVER_CONFIG_INIT(&config, nullptr); + WdfDriverCreate_t* WdfDriverCreate = (WdfDriverCreate_t*)UsersimWdfFunctions[WdfDriverCreateTableIndex]; + WDFDRIVER driver = nullptr; + DECLARE_CONST_UNICODE_STRING(registry_path, L""); + NTSTATUS status = WdfDriverCreate(UsersimWdfDriverGlobals, &driver_object, ®istry_path, nullptr, &config, &driver); + REQUIRE(status == STATUS_SUCCESS); + + // Allocate control device object. + WdfControlDeviceInitAllocate_t* WdfControlDeviceInitAllocate = + (WdfControlDeviceInitAllocate_t*)UsersimWdfFunctions[WdfControlDeviceInitAllocateTableIndex]; + DECLARE_CONST_UNICODE_STRING(security_descriptor, L""); + PWDFDEVICE_INIT init = WdfControlDeviceInitAllocate(UsersimWdfDriverGlobals, driver, &security_descriptor); + REQUIRE(init != nullptr); + + // Set device type. + WdfDeviceInitSetDeviceType_t* WdfDeviceInitSetDeviceType = + (WdfDeviceInitSetDeviceType_t*)UsersimWdfFunctions[WdfDeviceInitSetDeviceTypeTableIndex]; + WdfDeviceInitSetDeviceType(UsersimWdfDriverGlobals, init, FILE_DEVICE_NULL); + + // Set device characteristics. + WdfDeviceInitSetCharacteristics_t* WdfDeviceInitSetCharacteristics = + (WdfDeviceInitSetCharacteristics_t*)UsersimWdfFunctions[WdfDeviceInitSetCharacteristicsTableIndex]; + WdfDeviceInitSetCharacteristics(UsersimWdfDriverGlobals, init, FILE_DEVICE_SECURE_OPEN, FALSE); + + // Set device name. + UNICODE_STRING device_name; + RtlInitUnicodeString(&device_name, L"test device name"); + WdfDeviceInitAssignName_t* WdfDeviceInitAssignName = + (WdfDeviceInitAssignName_t*)UsersimWdfFunctions[WdfDeviceInitAssignNameTableIndex]; + status = WdfDeviceInitAssignName(UsersimWdfDriverGlobals, init, &device_name); + REQUIRE(status == STATUS_SUCCESS); + + // Set file object config. + WDF_OBJECT_ATTRIBUTES attributes = {0}; + WDF_FILEOBJECT_CONFIG file_object_config = {0}; + WdfDeviceInitSetFileObjectConfig_t* WdfDeviceInitSetFileObjectConfig = + (WdfDeviceInitSetFileObjectConfig_t*)UsersimWdfFunctions[WdfDeviceInitSetFileObjectConfigTableIndex]; + WdfDeviceInitSetFileObjectConfig(UsersimWdfDriverGlobals, init, &file_object_config, &attributes); + + WdfDeviceInitAssignWdmIrpPreprocessCallback_t* WdfDeviceInitAssignWdmIrpPreprocessCallback = + (WdfDeviceInitAssignWdmIrpPreprocessCallback_t*)UsersimWdfFunctions[WdfDeviceInitAssignWdmIrpPreprocessCallbackTableIndex]; + status = WdfDeviceInitAssignWdmIrpPreprocessCallback( + UsersimWdfDriverGlobals, init, driver_query_volume_information, IRP_MJ_QUERY_VOLUME_INFORMATION, NULL, 0); + REQUIRE(status == STATUS_SUCCESS); + + // Create device. + WDFDEVICE device = nullptr; + WdfDeviceCreate_t* WdfDeviceCreate = (WdfDeviceCreate_t*)UsersimWdfFunctions[WdfDeviceCreateTableIndex]; + status = WdfDeviceCreate(UsersimWdfDriverGlobals, &init, nullptr, &device); + REQUIRE(status == STATUS_SUCCESS); + REQUIRE(device != nullptr); + + // Create symbolic link for control object for user mode. + UNICODE_STRING symbolic_device_name; + RtlInitUnicodeString(&symbolic_device_name, L"symbolic device name"); + WdfDeviceCreateSymbolicLink_t* WdfDeviceCreateSymbolicLink = + (WdfDeviceCreateSymbolicLink_t*)UsersimWdfFunctions[WdfDeviceCreateSymbolicLinkTableIndex]; + status = WdfDeviceCreateSymbolicLink(UsersimWdfDriverGlobals, device, &symbolic_device_name); + REQUIRE(status == STATUS_SUCCESS); + + // Create an I/O queue. + WDF_IO_QUEUE_CONFIG io_queue_configuration = {0}; + WdfIoQueueCreate_t* WdfIoQueueCreate = (WdfIoQueueCreate_t*)UsersimWdfFunctions[WdfIoQueueCreateTableIndex]; +#pragma warning(suppress:28193) // status must be inspected + status = WdfIoQueueCreate( + UsersimWdfDriverGlobals, device, &io_queue_configuration, WDF_NO_OBJECT_ATTRIBUTES, WDF_NO_HANDLE); + REQUIRE(status == STATUS_SUCCESS); + + // Free device, which will free the control device object. + WdfObjectDelete_t* WdfObjectDelete = (WdfObjectDelete_t*)UsersimWdfFunctions[WdfObjectDeleteTableIndex]; + WdfObjectDelete(UsersimWdfDriverGlobals, device); + + UsersimWdfDriverGlobals->Driver = nullptr; +} \ No newline at end of file diff --git a/usersim_dll_skeleton/dll_skeleton.c b/usersim_dll_skeleton/dll_skeleton.c index 40b94ae..b105795 100644 --- a/usersim_dll_skeleton/dll_skeleton.c +++ b/usersim_dll_skeleton/dll_skeleton.c @@ -21,11 +21,7 @@ __declspec(dllimport) const WDFFUNC* UsersimWdfFunctions; PWDF_DRIVER_GLOBALS WdfDriverGlobals = NULL; const WDFFUNC* WdfFunctions_01015 = NULL; - - struct _driver_object - { - int dummy; - }; +static DRIVER_OBJECT _driver_object = {0}; NTSTATUS UsersimStartDriver() @@ -33,18 +29,18 @@ const WDFFUNC* WdfFunctions_01015 = NULL; WdfDriverGlobals = UsersimWdfDriverGlobals; WdfFunctions_01015 = UsersimWdfFunctions; - DRIVER_OBJECT driver_object = {0}; UNICODE_STRING registry_path = {0}; - return DriverEntry(&driver_object, ®istry_path); + return DriverEntry(&_driver_object, ®istry_path); } void UsersimStopDriver() { - WDFDRIVER driver = WdfDriverGlobals->Driver; - if (driver.config.EvtDriverUnload != NULL) { - driver.config.EvtDriverUnload(driver); + DRIVER_OBJECT* driver = (DRIVER_OBJECT*)WdfDriverGlobals->Driver; + if (driver->config.EvtDriverUnload != NULL) { + driver->config.EvtDriverUnload(driver); } + WdfDriverGlobals->Driver = NULL; } BOOL APIENTRY