Co-authored-by: Dave Thaler <dthaler@microsoft.com> Co-authored-by: Alan Jowett <alanjo@microsoft.com>
This commit is contained in:
Родитель
faa8197e6f
Коммит
6af44399e2
|
@ -71,116 +71,128 @@ static _Function_class_(EVT_WDF_DRIVER_UNLOAD) _IRQL_requires_same_
|
|||
ebpf_core_terminate();
|
||||
}
|
||||
|
||||
//
|
||||
// Create a basic WDF driver, set up the device object
|
||||
// for a callout driver and setup the ioctl surface
|
||||
//
|
||||
static NTSTATUS
|
||||
_ebpf_driver_initialize_objects(
|
||||
_Inout_ DRIVER_OBJECT* driver_object,
|
||||
_In_ const UNICODE_STRING* registry_path,
|
||||
_Out_ WDFDRIVER* driver,
|
||||
_Out_ WDFDEVICE* device)
|
||||
static _Check_return_ NTSTATUS
|
||||
_ebpf_driver_initialize_device(WDFDRIVER driver_handle, _Out_ WDFDEVICE* device)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDF_DRIVER_CONFIG driver_configuration;
|
||||
PWDFDEVICE_INIT device_initialize = NULL;
|
||||
WDF_IO_QUEUE_CONFIG io_queue_configuration;
|
||||
UNICODE_STRING ebpf_device_name;
|
||||
UNICODE_STRING ebpf_symbolic_device_name;
|
||||
BOOLEAN device_create_flag = FALSE;
|
||||
WDF_OBJECT_ATTRIBUTES attributes;
|
||||
UNICODE_STRING ebpf_device_name;
|
||||
WDF_FILEOBJECT_CONFIG file_object_config;
|
||||
UNICODE_STRING ebpf_symbolic_device_name;
|
||||
|
||||
WDF_DRIVER_CONFIG_INIT(&driver_configuration, WDF_NO_EVENT_CALLBACK);
|
||||
|
||||
// Allow access to kernel/system, administrators, and ebpfsvc only.
|
||||
DECLARE_CONST_UNICODE_STRING(security_descriptor, EBPF_EXECUTION_CONTEXT_DEVICE_SDDL);
|
||||
|
||||
driver_configuration.DriverInitFlags |= WdfDriverInitNonPnpDriver;
|
||||
driver_configuration.EvtDriverUnload = _ebpf_driver_unload;
|
||||
|
||||
status = WdfDriverCreate(driver_object, registry_path, WDF_NO_OBJECT_ATTRIBUTES, &driver_configuration, driver);
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
device_initialize = WdfControlDeviceInitAllocate(
|
||||
*driver,
|
||||
&security_descriptor // only kernel/system, administrators, and ebpfsvc.
|
||||
);
|
||||
device_initialize = WdfControlDeviceInitAllocate(driver_handle, &security_descriptor);
|
||||
if (!device_initialize) {
|
||||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||||
EBPF_LOG_NTSTATUS_API_FAILURE(EBPF_TRACELOG_KEYWORD_ERROR, "WdfControlDeviceInitAllocate", status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
WdfDeviceInitSetDeviceType(device_initialize, FILE_DEVICE_NULL);
|
||||
|
||||
WdfDeviceInitSetCharacteristics(device_initialize, FILE_DEVICE_SECURE_OPEN, FALSE);
|
||||
|
||||
WdfDeviceInitSetCharacteristics(device_initialize, FILE_AUTOGENERATED_DEVICE_NAME, TRUE);
|
||||
|
||||
RtlInitUnicodeString(&ebpf_device_name, EBPF_DEVICE_NAME);
|
||||
status = WdfDeviceInitAssignName(device_initialize, &ebpf_device_name);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
EBPF_LOG_NTSTATUS_API_FAILURE(EBPF_TRACELOG_KEYWORD_ERROR, "WdfDeviceInitAssignName", status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
||||
attributes.SynchronizationScope = WdfSynchronizationScopeNone;
|
||||
WDF_FILEOBJECT_CONFIG_INIT(
|
||||
&file_object_config,
|
||||
NULL,
|
||||
_ebpf_driver_file_close,
|
||||
WDF_NO_EVENT_CALLBACK // No cleanup callback function
|
||||
);
|
||||
WDF_FILEOBJECT_CONFIG_INIT(&file_object_config, NULL, _ebpf_driver_file_close, WDF_NO_EVENT_CALLBACK);
|
||||
WdfDeviceInitSetFileObjectConfig(device_initialize, &file_object_config, &attributes);
|
||||
|
||||
// WDF framework doesn't handle IRP_MJ_QUERY_VOLUME_INFORMATION.
|
||||
// Register a handler for this IRP.
|
||||
// WDF framework doesn't handle IRP_MJ_QUERY_VOLUME_INFORMATION so register a handler for this IRP.
|
||||
status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
|
||||
device_initialize, _ebpf_driver_query_volume_information, IRP_MJ_QUERY_VOLUME_INFORMATION, NULL, 0);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
EBPF_LOG_NTSTATUS_API_FAILURE(
|
||||
EBPF_TRACELOG_KEYWORD_ERROR, "WdfDeviceInitAssignWdmIrpPreprocessCallback", status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
status = WdfDeviceCreate(&device_initialize, WDF_NO_OBJECT_ATTRIBUTES, device);
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
// do not free if any other call
|
||||
// after WdfDeviceCreate fails.
|
||||
|
||||
// Do not free if any other call after WdfDeviceCreate fails.
|
||||
WdfDeviceInitFree(device_initialize);
|
||||
device_initialize = NULL;
|
||||
EBPF_LOG_NTSTATUS_API_FAILURE(EBPF_TRACELOG_KEYWORD_ERROR, "WdfDeviceCreate", status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
// Create symbolic link for control object for user mode.
|
||||
RtlInitUnicodeString(&ebpf_symbolic_device_name, EBPF_SYMBOLIC_DEVICE_NAME);
|
||||
status = WdfDeviceCreateSymbolicLink(*device, &ebpf_symbolic_device_name);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
EBPF_LOG_NTSTATUS_API_FAILURE(EBPF_TRACELOG_KEYWORD_ERROR, "WdfDeviceCreateSymbolicLink", status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Exit:
|
||||
return status;
|
||||
}
|
||||
|
||||
// Create a basic WDF driver, set up the device object for a callout driver and set up the ioctl surface.
|
||||
static _Check_return_ NTSTATUS
|
||||
_ebpf_driver_initialize_objects(
|
||||
_Inout_ DRIVER_OBJECT* driver_object,
|
||||
_In_ const UNICODE_STRING* registry_path,
|
||||
_Out_ WDFDRIVER* driver_handle,
|
||||
_Out_ WDFDEVICE* device)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDF_DRIVER_CONFIG driver_configuration;
|
||||
WDF_IO_QUEUE_CONFIG io_queue_configuration;
|
||||
BOOLEAN device_create_flag = FALSE;
|
||||
|
||||
// IMPORTANT NOTE: The choice of implementing part of the driver initialization in another function
|
||||
// (_ebpf_driver_initialize_device()) is deliberate. We perform a lot of standard WDF driver initialization here
|
||||
// (and ebpf support code as well) and consequently need quite a few local variables (most of 'struct' type). Some
|
||||
// of these are quite large and end up chewing up a lot of stack space. This causes Code Analysis tools to flag
|
||||
// compile-time stack overflow errors when these variables (together) exceed the default stack size of 1024 bytes.
|
||||
//
|
||||
// This split between multiple functions ensures we don't hit this condition. Please keep this mind when
|
||||
// refactoring/enhancing this function.
|
||||
//
|
||||
// One way to ensure this would be to run Code Analysis tools locally to catch such issues very early rather than
|
||||
// wait for them to be flagged at the CI/CD gate during PR validation.
|
||||
//
|
||||
// OTOH, the CI/CD pipeline performs this check on a 'Draft PR' as well, so that's an option too.
|
||||
|
||||
WDF_DRIVER_CONFIG_INIT(&driver_configuration, WDF_NO_EVENT_CALLBACK);
|
||||
driver_configuration.DriverInitFlags |= WdfDriverInitNonPnpDriver;
|
||||
driver_configuration.EvtDriverUnload = _ebpf_driver_unload;
|
||||
status =
|
||||
WdfDriverCreate(driver_object, registry_path, WDF_NO_OBJECT_ATTRIBUTES, &driver_configuration, driver_handle);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
EBPF_LOG_NTSTATUS_API_FAILURE(EBPF_TRACELOG_KEYWORD_ERROR, "WdfDriverCreate", status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
status = _ebpf_driver_initialize_device(*driver_handle, device);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
EBPF_LOG_MESSAGE_NTSTATUS(
|
||||
EBPF_TRACELOG_LEVEL_CRITICAL, EBPF_TRACELOG_KEYWORD_ERROR, (char*)"_ebpf_driver_initialize_device", status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
device_create_flag = TRUE;
|
||||
|
||||
// Create symbolic link for control object for user mode.
|
||||
RtlInitUnicodeString(&ebpf_symbolic_device_name, EBPF_SYMBOLIC_DEVICE_NAME);
|
||||
status = WdfDeviceCreateSymbolicLink(*device, &ebpf_symbolic_device_name);
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
// parallel default queue
|
||||
// Create default queue.
|
||||
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&io_queue_configuration, WdfIoQueueDispatchParallel);
|
||||
|
||||
io_queue_configuration.EvtIoDeviceControl = _ebpf_driver_io_device_control;
|
||||
|
||||
status = WdfIoQueueCreate(
|
||||
*device,
|
||||
&io_queue_configuration,
|
||||
WDF_NO_OBJECT_ATTRIBUTES,
|
||||
WDF_NO_HANDLE // pointer to default queue
|
||||
);
|
||||
status = WdfIoQueueCreate(*device, &io_queue_configuration, WDF_NO_OBJECT_ATTRIBUTES, WDF_NO_HANDLE);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
EBPF_LOG_NTSTATUS_API_FAILURE(EBPF_TRACELOG_KEYWORD_ERROR, "WdfIoQueueCreate", status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
status = ebpf_result_to_ntstatus(ebpf_core_initiate());
|
||||
if (!NT_SUCCESS(status)) {
|
||||
EBPF_LOG_NTSTATUS_API_FAILURE(EBPF_TRACELOG_KEYWORD_ERROR, "ebpf_core_initiate", status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
|
@ -189,10 +201,8 @@ _ebpf_driver_initialize_objects(
|
|||
Exit:
|
||||
if (!NT_SUCCESS(status)) {
|
||||
if (device_create_flag && device != NULL) {
|
||||
//
|
||||
// Release the reference on the newly created object, since
|
||||
// we couldn't initialize it.
|
||||
//
|
||||
|
||||
// Release the reference on the newly created object, since we couldn't initialize it.
|
||||
WdfObjectDelete(*device);
|
||||
}
|
||||
}
|
||||
|
@ -259,12 +269,14 @@ _ebpf_driver_io_device_control(
|
|||
);
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "EbpfCore: Input buffer failure %d\n", status));
|
||||
EBPF_LOG_NTSTATUS_API_FAILURE(EBPF_TRACELOG_KEYWORD_ERROR, "WdfRequestRetrieveInputBuffer", status);
|
||||
goto Done;
|
||||
}
|
||||
|
||||
if (input_buffer == NULL) {
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
EBPF_LOG_NTSTATUS_API_FAILURE_MESSAGE(
|
||||
EBPF_TRACELOG_KEYWORD_ERROR, "WdfRequestRetrieveInputBuffer", status, "Input buffer is null");
|
||||
goto Done;
|
||||
}
|
||||
|
||||
|
@ -275,14 +287,19 @@ _ebpf_driver_io_device_control(
|
|||
|
||||
user_request = input_buffer;
|
||||
if (actual_input_length < sizeof(struct _ebpf_operation_header)) {
|
||||
EBPF_LOG_MESSAGE(
|
||||
EBPF_TRACELOG_LEVEL_ERROR, EBPF_TRACELOG_KEYWORD_ERROR, "Input buffer is too small");
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
goto Done;
|
||||
}
|
||||
|
||||
status = ebpf_result_to_ntstatus(ebpf_core_get_protocol_handler_properties(
|
||||
user_request->id, &minimum_request_size, &minimum_reply_size, &async));
|
||||
if (status != STATUS_SUCCESS)
|
||||
if (status != STATUS_SUCCESS) {
|
||||
EBPF_LOG_NTSTATUS_API_FAILURE(
|
||||
EBPF_TRACELOG_KEYWORD_ERROR, "ebpf_core_get_protocol_handler_properties", status);
|
||||
goto Done;
|
||||
}
|
||||
|
||||
// Be aware: Input and output buffer point to the same memory.
|
||||
if (minimum_reply_size > 0) {
|
||||
|
@ -290,16 +307,23 @@ _ebpf_driver_io_device_control(
|
|||
status = WdfRequestRetrieveOutputBuffer(
|
||||
request, output_buffer_length, &output_buffer, &actual_output_length);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
KdPrintEx(
|
||||
(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "EbpfCore: Output buffer failure %d\n", status));
|
||||
EBPF_LOG_NTSTATUS_API_FAILURE(
|
||||
EBPF_TRACELOG_KEYWORD_ERROR, "WdfRequestRetrieveOutputBuffer", status);
|
||||
goto Done;
|
||||
}
|
||||
if (output_buffer == NULL) {
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
EBPF_LOG_NTSTATUS_API_FAILURE_MESSAGE(
|
||||
EBPF_TRACELOG_KEYWORD_ERROR,
|
||||
"WdfRequestRetrieveOutputBuffer",
|
||||
status,
|
||||
"Output buffer is null");
|
||||
goto Done;
|
||||
}
|
||||
|
||||
if (actual_output_length < minimum_reply_size) {
|
||||
EBPF_LOG_MESSAGE(
|
||||
EBPF_TRACELOG_LEVEL_ERROR, EBPF_TRACELOG_KEYWORD_ERROR, "Output buffer is too small");
|
||||
status = STATUS_BUFFER_TOO_SMALL;
|
||||
goto Done;
|
||||
}
|
||||
|
@ -321,10 +345,14 @@ _ebpf_driver_io_device_control(
|
|||
(uint16_t)actual_output_length,
|
||||
async_context,
|
||||
_ebpf_driver_io_device_control_complete));
|
||||
|
||||
if (status != STATUS_SUCCESS) {
|
||||
EBPF_LOG_NTSTATUS_API_FAILURE(
|
||||
EBPF_TRACELOG_KEYWORD_ERROR, "ebpf_core_invoke_protocol_handler", status);
|
||||
}
|
||||
goto Done;
|
||||
}
|
||||
} else {
|
||||
EBPF_LOG_MESSAGE(EBPF_TRACELOG_LEVEL_ERROR, EBPF_TRACELOG_KEYWORD_ERROR, "Zero length input buffer");
|
||||
status = STATUS_INVALID_PARAMETER;
|
||||
goto Done;
|
||||
}
|
||||
|
@ -350,23 +378,35 @@ NTSTATUS
|
|||
DriverEntry(_In_ DRIVER_OBJECT* driver_object, _In_ UNICODE_STRING* registry_path)
|
||||
{
|
||||
NTSTATUS status;
|
||||
WDFDRIVER driver;
|
||||
WDFDRIVER driver_handle;
|
||||
WDFDEVICE device;
|
||||
|
||||
status = ebpf_trace_initiate();
|
||||
if (!NT_SUCCESS(status)) {
|
||||
|
||||
// Fail silently as there is no other mechanism to indicate this failure. Note that in this case, the
|
||||
// EBPF_LOG_EXIT() call at the end will not log anything either.
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
EBPF_LOG_ENTRY();
|
||||
|
||||
// Request NX Non-Paged Pool when available
|
||||
ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
|
||||
|
||||
KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "EbpfCore: DriverEntry\n"));
|
||||
|
||||
status = _ebpf_driver_initialize_objects(driver_object, registry_path, &driver, &device);
|
||||
|
||||
status = _ebpf_driver_initialize_objects(driver_object, registry_path, &driver_handle, &device);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
EBPF_LOG_MESSAGE_NTSTATUS(
|
||||
EBPF_TRACELOG_LEVEL_CRITICAL,
|
||||
EBPF_TRACELOG_KEYWORD_ERROR,
|
||||
(char*)"_ebpf_driver_initialize_objects failed",
|
||||
status);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
_ebpf_driver_device_object = WdfDeviceWdmGetDeviceObject(device);
|
||||
|
||||
Exit:
|
||||
EBPF_LOG_EXIT();
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
|
@ -1208,6 +1208,15 @@ extern "C"
|
|||
TraceLoggingKeyword((keyword)), \
|
||||
TraceLoggingString(message, "Message"));
|
||||
|
||||
#define EBPF_LOG_MESSAGE_NTSTATUS(trace_level, keyword, message, status) \
|
||||
TraceLoggingWrite( \
|
||||
ebpf_tracelog_provider, \
|
||||
EBPF_TRACELOG_EVENT_GENERIC_MESSAGE, \
|
||||
TraceLoggingLevel(trace_level), \
|
||||
TraceLoggingKeyword((keyword)), \
|
||||
TraceLoggingString(message, "Message"), \
|
||||
TraceLoggingNTStatus(status));
|
||||
|
||||
#define EBPF_LOG_MESSAGE_UTF8_STRING(trace_level, keyword, message, string) \
|
||||
TraceLoggingWrite( \
|
||||
ebpf_tracelog_provider, \
|
||||
|
@ -1354,6 +1363,16 @@ extern "C"
|
|||
TraceLoggingString(#api, "api"), \
|
||||
TraceLoggingNTStatus(status));
|
||||
|
||||
#define EBPF_LOG_NTSTATUS_API_FAILURE_MESSAGE(keyword, api, status, message) \
|
||||
TraceLoggingWrite( \
|
||||
ebpf_tracelog_provider, \
|
||||
EBPF_TRACELOG_EVENT_API_ERROR, \
|
||||
TraceLoggingLevel(EBPF_TRACELOG_LEVEL_ERROR), \
|
||||
TraceLoggingKeyword((keyword)), \
|
||||
TraceLoggingString(#api, "api"), \
|
||||
TraceLoggingNTStatus(status), \
|
||||
TraceLoggingString(message, "Message"));
|
||||
|
||||
#define EBPF_LOG_NTSTATUS_WSTRING_API(keyword, wstring, api, status) \
|
||||
TraceLoggingWrite( \
|
||||
ebpf_tracelog_provider, \
|
||||
|
|
Загрузка…
Ссылка в новой задаче