Skip to content

Commit

Permalink
Add Wdf* APIs required by ebpf-for-windows
Browse files Browse the repository at this point in the history
Add cache-aligned allocations (fixes #31)

Signed-off-by: Dave Thaler <[email protected]>
  • Loading branch information
dthaler committed Aug 4, 2023
1 parent 7ce8f1a commit 9c7fb3a
Show file tree
Hide file tree
Showing 15 changed files with 549 additions and 238 deletions.
96 changes: 96 additions & 0 deletions inc/usersim/ex.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,80 @@ USERSIM_API _Ret_maybenull_ void*
ExAllocatePoolWithTagCPP(
_In_ __drv_strictTypeMatch(__drv_typeExpr) POOL_TYPE pool_type, SIZE_T number_of_bytes, ULONG tag);

/**
* @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);

USERSIM_API _Ret_maybenull_ void*
ExAllocatePoolUninitializedCPP(_In_ POOL_TYPE pool_type, _In_ size_t number_of_bytes, _In_ unsigned long tag);

Expand All @@ -207,4 +281,26 @@ ExRaiseAccessViolationCPP();
USERSIM_API void
ExRaiseDatatypeMisalignmentCPP();

void usersim_initialize_ex(bool leak_detector);
void usersim_clean_up_ex();

#ifdef __cplusplus
#include <memory>
namespace usersim_helper {

struct _usersim_free_functor
{
void
operator()(void* memory)
{
usersim_free(memory);
}
};

typedef std::unique_ptr<void, _usersim_free_functor> usersim_memory_ptr;

} // namespace usersim_helper

#endif

#endif
8 changes: 7 additions & 1 deletion inc/usersim/io.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
8 changes: 3 additions & 5 deletions inc/usersim/wdf.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@ 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 _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);
Expand All @@ -44,10 +42,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)

Expand Down
1 change: 1 addition & 0 deletions src/etw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "platform.h"
#include "usersim/etw.h"
#include "usersim/ex.h"

typedef struct
{
Expand Down
188 changes: 186 additions & 2 deletions src/ex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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.
Expand Down Expand Up @@ -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*
Expand All @@ -318,7 +322,170 @@ 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.
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<uintptr_t>(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<uintptr_t>(memory));
_usersim_leak_detector_ptr->register_allocation(reinterpret_cast<uintptr_t>(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<uintptr_t>(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*
Expand Down Expand Up @@ -409,4 +576,21 @@ void
ExRaiseDatatypeMisalignment()
{
ExRaiseDatatypeMisalignmentCPP();
}

void
usersim_initialize_ex(bool leak_detector)
{
if (leak_detector) {
_usersim_leak_detector_ptr = std::make_unique<usersim_leak_detector_t>();
}
}

void
usersim_clean_up_ex()
{
if (_usersim_leak_detector_ptr) {
_usersim_leak_detector_ptr->dump_leaks();
_usersim_leak_detector_ptr.reset();
}
}
Loading

0 comments on commit 9c7fb3a

Please sign in to comment.