Add Tracelogging to ebfcore driver (#1565) (#1566)

Co-authored-by: Dave Thaler <dthaler@microsoft.com>
Co-authored-by: Alan Jowett <alanjo@microsoft.com>
This commit is contained in:
Dhiren Vispute 2022-11-10 02:17:32 -08:00 коммит произвёл GitHub
Родитель faa8197e6f
Коммит 6af44399e2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 137 добавлений и 78 удалений

Просмотреть файл

@ -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, \