From f5f3559ae95df4e8fe86cb6c319f45a45a3e85e3 Mon Sep 17 00:00:00 2001 From: basil00 Date: Mon, 16 Oct 2017 22:56:38 +0800 Subject: [PATCH] Various improvements - Support NonPagedPoolNx for Windows 8, 10. - More aggressive context locking - Set MAXWORKERS=1 (seems to perform best). --- sys/windivert.c | 503 +++++++++++++++++++++++++++++------------------- 1 file changed, 304 insertions(+), 199 deletions(-) diff --git a/sys/windivert.c b/sys/windivert.c index df8f486..1e5bae3 100644 --- a/sys/windivert.c +++ b/sys/windivert.c @@ -60,10 +60,6 @@ static void DEBUG(PCCH format, ...) DbgPrint("WINDIVERT: %s\n", buf); va_end(args); } -#else // DEBUG_ON -#define DEBUG(format, ...) -#endif - static void DEBUG_ERROR(PCCH format, NTSTATUS status, ...) { va_list args; @@ -77,6 +73,10 @@ static void DEBUG_ERROR(PCCH format, NTSTATUS status, ...) DbgPrint("WINDIVERT: *** ERROR ***: (status = %x): %s\n", status, buf); va_end(args); } +#else // DEBUG_ON +#define DEBUG(format, ...) +#define DEBUG_ERROR(format, status, ...) +#endif #define WINDIVERT_TAG 'viDW' @@ -104,10 +104,9 @@ typedef struct filter_s *filter_t; /* * WinDivert context information. */ -#define WINDIVERT_CONTEXT_MAGIC 0x1C1E48D280D7F487ull #define WINDIVERT_CONTEXT_SIZE (sizeof(struct context_s)) #define WINDIVERT_CONTEXT_MAXLAYERS 4 -#define WINDIVERT_CONTEXT_MAXWORKERS 2 +#define WINDIVERT_CONTEXT_MAXWORKERS 1 #define WINDIVERT_CONTEXT_OUTBOUND_IPV4_LAYER 0 #define WINDIVERT_CONTEXT_INBOUND_IPV4_LAYER 1 #define WINDIVERT_CONTEXT_OUTBOUND_IPV6_LAYER 2 @@ -122,7 +121,6 @@ typedef enum } context_state_t; struct context_s { - UINT64 magic; // WINDIVERT_CONTEXT_MAGIC context_state_t state; // Context's state. KSPIN_LOCK lock; // Context-wide lock. WDFDEVICE device; // Context's device. @@ -139,11 +137,8 @@ struct context_s WDFWORKITEM workers[WINDIVERT_CONTEXT_MAXWORKERS]; // Read workers. UINT8 worker_curr; // Current read worker. - UINT8 layer_0; // Context's layer (initial). UINT8 layer; // Context's layer. - UINT64 flags_0; // Context's flags (initial). UINT64 flags; // Context's flags. - UINT32 priority_0; // Context's priority (initial). UINT32 priority; // Context's priority. GUID callout_guid[WINDIVERT_CONTEXT_MAXLAYERS]; // Callout GUIDs. @@ -151,7 +146,7 @@ struct context_s // Filter GUIDs. BOOL installed[WINDIVERT_CONTEXT_MAXLAYERS]; // What is installed? - LONG filter_on; // Is filter on? + BOOL on; // Is filtering on? HANDLE engine_handle; // WFP engine handle. filter_t filter; // Packet filter. }; @@ -339,6 +334,7 @@ static NDIS_HANDLE nb_pool_handle = NULL; static HANDLE engine_handle = NULL; static LONG priority_counter = 0; static ULONGLONG counts_per_ms = 0; +static POOL_TYPE non_paged_pool = NonPagedPool; /* * Priorities. @@ -364,16 +360,15 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, static NTSTATUS windivert_read(context_t context, WDFREQUEST request); extern VOID windivert_worker(IN WDFWORKITEM item); static void windivert_read_service(context_t context); -static BOOLEAN windivert_context_verify(context_t context, - context_state_t state); extern VOID windivert_create(IN WDFDEVICE device, IN WDFREQUEST request, IN WDFFILEOBJECT object); static NTSTATUS windivert_install_sublayer(layer_t layer); -static NTSTATUS windivert_install_callouts(context_t context, BOOL is_inbound, - BOOL is_outbound, BOOL is_ipv4, BOOL is_ipv6); +static NTSTATUS windivert_install_callouts(context_t context, UINT8 layer, + BOOL is_inbound, BOOL is_outbound, BOOL is_ipv4, BOOL is_ipv6); static NTSTATUS windivert_install_callout(context_t context, UINT idx, layer_t layer); -static void windivert_uninstall_callouts(context_t context); +static void windivert_uninstall_callouts(context_t context, + context_state_t state); extern VOID windivert_cleanup(IN WDFFILEOBJECT object); extern VOID windivert_close(IN WDFFILEOBJECT object); extern VOID windivert_destroy(IN WDFOBJECT object); @@ -551,6 +546,22 @@ static struct layer_s layer_forward_network_ipv6_0 = }; static layer_t layer_forward_network_ipv6 = &layer_forward_network_ipv6_0; +/* + * WinDivert malloc/free. + */ +static PVOID windivert_malloc(SIZE_T size, BOOL paged) +{ + POOL_TYPE pool = (paged? PagedPool: non_paged_pool); + return ExAllocatePoolWithTag(pool, size, WINDIVERT_TAG); +} +static VOID windivert_free(PVOID ptr) +{ + if (ptr != NULL) + { + ExFreePoolWithTag(ptr, WINDIVERT_TAG); + } +} + /* * WinDivert driver entry routine. */ @@ -567,6 +578,7 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj, WDF_OBJECT_ATTRIBUTES obj_attrs; NET_BUFFER_LIST_POOL_PARAMETERS nbl_pool_params; NET_BUFFER_POOL_PARAMETERS nb_pool_params; + RTL_OSVERSIONINFOW version; LARGE_INTEGER freq; NTSTATUS status; DECLARE_CONST_UNICODE_STRING(device_name, @@ -576,6 +588,17 @@ extern NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver_obj, DEBUG("LOAD: loading WinDivert driver"); + // Use the "no execute" pool if available: + status = RtlGetVersion(&version); + if (NT_SUCCESS(status)) + { + if (version.dwMajorVersion > 6 || + (version.dwMajorVersion == 6 && version.dwMinorVersion >= 2)) + { + non_paged_pool = (POOL_TYPE)512; // NonPagedPoolNx (documented) + } + } + // Initialize timer info. KeQueryPerformanceCounter(&freq); counts_per_ms = (ULONGLONG)freq.QuadPart / 1000; @@ -873,34 +896,6 @@ static NTSTATUS windivert_install_sublayer(layer_t layer) return status; } -/* - * WinDivert context verify. - */ -static BOOLEAN windivert_context_verify(context_t context, - context_state_t state) -{ - if (context == NULL) - { - DEBUG_ERROR("failed to verify context; context is NULL", - STATUS_INVALID_HANDLE); - return FALSE; - } - if (context->magic != WINDIVERT_CONTEXT_MAGIC) - { - DEBUG_ERROR("failed to verify context; invalid magic number", - STATUS_INVALID_HANDLE); - return FALSE; - } - if (context->state != state) - { - DEBUG_ERROR("failed to verify context; expected context state %x, " - "found context state %x", STATUS_INVALID_HANDLE, state, - context->state); - return FALSE; - } - return TRUE; -} - /* * WinDivert create routine. */ @@ -918,7 +913,6 @@ extern VOID windivert_create(IN WDFDEVICE device, IN WDFREQUEST request, DEBUG("CREATE: creating a new WinDivert context (context=%p)", context); // Initialise the new context: - context->magic = WINDIVERT_CONTEXT_MAGIC; context->state = WINDIVERT_CONTEXT_STATE_OPENING; context->device = device; context->object = object; @@ -929,14 +923,10 @@ extern VOID windivert_create(IN WDFDEVICE device, IN WDFREQUEST request, context->packet_queue_maxcounts = WINDIVERT_PARAM_QUEUE_TIME_DEFAULT * counts_per_ms; context->packet_queue_maxtime = WINDIVERT_PARAM_QUEUE_TIME_DEFAULT; - context->layer_0 = WINDIVERT_LAYER_DEFAULT; - context->layer = WINDIVERT_LAYER_DEFAULT; - context->flags_0 = 0; - context->flags = 0; - context->priority_0 = - WINDIVERT_CONTEXT_PRIORITY(WINDIVERT_PRIORITY_DEFAULT); - context->priority = context->priority_0; - context->filter = NULL; + context->layer = WINDIVERT_LAYER_DEFAULT; + context->flags = 0; + context->priority = WINDIVERT_CONTEXT_PRIORITY(WINDIVERT_PRIORITY_DEFAULT); + context->filter = NULL; for (i = 0; i < WINDIVERT_CONTEXT_MAXWORKERS; i++) { context->workers[i] = NULL; @@ -946,7 +936,7 @@ extern VOID windivert_create(IN WDFDEVICE device, IN WDFREQUEST request, { context->installed[i] = FALSE; } - context->filter_on = FALSE; + context->on = FALSE; KeInitializeSpinLock(&context->lock); InitializeListHead(&context->work_queue); InitializeListHead(&context->packet_queue); @@ -1027,15 +1017,15 @@ windivert_create_exit: /* * Register all WFP callouts. */ -static NTSTATUS windivert_install_callouts(context_t context, BOOL is_inbound, - BOOL is_outbound, BOOL is_ipv4, BOOL is_ipv6) +static NTSTATUS windivert_install_callouts(context_t context, UINT8 layer, + BOOL is_inbound, BOOL is_outbound, BOOL is_ipv4, BOOL is_ipv6) { UINT8 i, j; layer_t layers[WINDIVERT_CONTEXT_MAXLAYERS]; NTSTATUS status; i = 0; - switch (context->layer) + switch (layer) { case WINDIVERT_LAYER_NETWORK: if (is_inbound && is_ipv4) @@ -1084,7 +1074,7 @@ windivert_install_callouts_exit: if (!NT_SUCCESS(status)) { - windivert_uninstall_callouts(context); + windivert_uninstall_callouts(context, WINDIVERT_CONTEXT_STATE_OPEN); } return status; @@ -1096,87 +1086,133 @@ windivert_install_callouts_exit: static NTSTATUS windivert_install_callout(context_t context, UINT idx, layer_t layer) { + KLOCK_QUEUE_HANDLE lock_handle; FWPS_CALLOUT0 scallout; FWPM_CALLOUT0 mcallout; FWPM_FILTER0 filter; UINT64 weight; + UINT32 priority; + GUID callout_guid, filter_guid; + WDFDEVICE device; + HANDLE engine_handle; NTSTATUS status; - weight = WINDIVERT_FILTER_WEIGHT(context->priority); + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_OPEN) + { + KeReleaseInStackQueuedSpinLock(&lock_handle); + status = STATUS_INVALID_DEVICE_STATE; + return status; + } + priority = context->priority; + callout_guid = context->callout_guid[idx]; + filter_guid = context->filter_guid[idx]; + device = context->device; + engine_handle = context->engine_handle; + KeReleaseInStackQueuedSpinLock(&lock_handle); + + weight = WINDIVERT_FILTER_WEIGHT(priority); RtlZeroMemory(&scallout, sizeof(scallout)); - scallout.calloutKey = context->callout_guid[idx]; + scallout.calloutKey = callout_guid; scallout.classifyFn = layer->callout; scallout.notifyFn = windivert_notify_callout; scallout.flowDeleteFn = NULL; RtlZeroMemory(&mcallout, sizeof(mcallout)); - mcallout.calloutKey = context->callout_guid[idx]; + mcallout.calloutKey = callout_guid; mcallout.displayData.name = layer->callout_name; mcallout.displayData.description = layer->callout_desc; mcallout.applicableLayer = layer->layer_guid; RtlZeroMemory(&filter, sizeof(filter)); - filter.filterKey = context->filter_guid[idx]; + filter.filterKey = filter_guid; filter.layerKey = layer->layer_guid; filter.displayData.name = layer->filter_name; filter.displayData.description = layer->filter_desc; filter.action.type = FWP_ACTION_CALLOUT_UNKNOWN; - filter.action.calloutKey = context->callout_guid[idx]; + filter.action.calloutKey = callout_guid; filter.subLayerKey = layer->sublayer_guid; filter.weight.type = FWP_UINT64; filter.weight.uint64 = &weight; filter.rawContext = (UINT64)context; - status = FwpsCalloutRegister0(WdfDeviceWdmGetDeviceObject(context->device), + status = FwpsCalloutRegister0(WdfDeviceWdmGetDeviceObject(device), &scallout, NULL); if (!NT_SUCCESS(status)) { DEBUG_ERROR("failed to install WFP callout", status); return status; } - status = FwpmTransactionBegin0(context->engine_handle, 0); + status = FwpmTransactionBegin0(engine_handle, 0); if (!NT_SUCCESS(status)) { DEBUG_ERROR("failed to begin WFP transaction", status); - FwpsCalloutUnregisterByKey0(&context->callout_guid[idx]); + FwpsCalloutUnregisterByKey0(&callout_guid); return status; } - status = FwpmCalloutAdd0(context->engine_handle, &mcallout, NULL, NULL); + status = FwpmCalloutAdd0(engine_handle, &mcallout, NULL, NULL); if (!NT_SUCCESS(status)) { DEBUG_ERROR("failed to add WFP callout", status); goto windivert_install_callout_error; } - status = FwpmFilterAdd0(context->engine_handle, &filter, NULL, NULL); + status = FwpmFilterAdd0(engine_handle, &filter, NULL, NULL); if (!NT_SUCCESS(status)) { DEBUG_ERROR("failed to add WFP filter", status); goto windivert_install_callout_error; } - status = FwpmTransactionCommit0(context->engine_handle); + status = FwpmTransactionCommit0(engine_handle); if (!NT_SUCCESS(status)) { DEBUG_ERROR("failed to commit WFP transaction", status); - FwpsCalloutUnregisterByKey0(&context->callout_guid[idx]); + FwpsCalloutUnregisterByKey0(&callout_guid); return status; } + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_OPEN) + { + KeReleaseInStackQueuedSpinLock(&lock_handle); + FwpsCalloutUnregisterByKey0(&callout_guid); + status = STATUS_INVALID_DEVICE_STATE; + return status; + } context->installed[idx] = TRUE; + KeReleaseInStackQueuedSpinLock(&lock_handle); + return STATUS_SUCCESS; windivert_install_callout_error: - FwpmTransactionAbort0(context->engine_handle); - FwpsCalloutUnregisterByKey0(&context->callout_guid[idx]); + FwpmTransactionAbort0(engine_handle); + FwpsCalloutUnregisterByKey0(&callout_guid); return status; } /* * WinDivert uninstall callouts routine. */ -static void windivert_uninstall_callouts(context_t context) +static void windivert_uninstall_callouts(context_t context, + context_state_t state) { + KLOCK_QUEUE_HANDLE lock_handle; UINT i; + HANDLE engine_handle; + BOOL installed; + GUID callout_guid, filter_guid; NTSTATUS status; - status = FwpmTransactionBegin0(context->engine_handle, 0); + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != state) + { +windivert_uninstall_callouts_error: + KeReleaseInStackQueuedSpinLock(&lock_handle); + status = STATUS_INVALID_DEVICE_STATE; + DEBUG_ERROR("failed to delete filters and callouts", status); + return; + } + engine_handle = context->engine_handle; + KeReleaseInStackQueuedSpinLock(&lock_handle); + + status = FwpmTransactionBegin0(engine_handle, 0); if (!NT_SUCCESS(status)) { // If the userspace app closes without closing the handle to @@ -1188,21 +1224,31 @@ static void windivert_uninstall_callouts(context_t context) } for (i = 0; i < WINDIVERT_CONTEXT_MAXLAYERS; i++) { - if (!context->installed[i]) + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != state) + { + KeReleaseInStackQueuedSpinLock(&lock_handle); + FwpmTransactionAbort0(engine_handle); + status = STATUS_INVALID_DEVICE_STATE; + DEBUG_ERROR("failed to delete filters and callouts", status); + return; + } + installed = context->installed[i]; + callout_guid = context->callout_guid[i]; + filter_guid = context->filter_guid[i]; + KeReleaseInStackQueuedSpinLock(&lock_handle); + + if (!installed) { continue; } - - status = FwpmFilterDeleteByKey0(context->engine_handle, - &context->filter_guid[i]); + status = FwpmFilterDeleteByKey0(engine_handle, &filter_guid); if (!NT_SUCCESS(status)) { DEBUG_ERROR("failed to delete filter", status); break; } - - status = FwpmCalloutDeleteByKey0(context->engine_handle, - &context->callout_guid[i]); + status = FwpmCalloutDeleteByKey0(engine_handle, &callout_guid); if (!NT_SUCCESS(status)) { DEBUG_ERROR("failed to delete callout", status); @@ -1211,10 +1257,10 @@ static void windivert_uninstall_callouts(context_t context) } if (!NT_SUCCESS(status)) { - FwpmTransactionAbort0(context->engine_handle); + FwpmTransactionAbort0(engine_handle); goto unregister_callouts; } - status = FwpmTransactionCommit0(context->engine_handle); + status = FwpmTransactionCommit0(engine_handle); if (!NT_SUCCESS(status)) { DEBUG_ERROR("failed to commit WFP transaction", status); @@ -1223,8 +1269,20 @@ static void windivert_uninstall_callouts(context_t context) unregister_callouts: for (i = 0; i < WINDIVERT_CONTEXT_MAXLAYERS; i++) { - FwpsCalloutUnregisterByKey0(&context->callout_guid[i]); + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != state) + { + goto windivert_uninstall_callouts_error; + } + installed = context->installed[i]; + callout_guid = context->callout_guid[i]; context->installed[i] = FALSE; + KeReleaseInStackQueuedSpinLock(&lock_handle); + if (!installed) + { + continue; + } + FwpsCalloutUnregisterByKey0(&callout_guid); } } @@ -1239,15 +1297,21 @@ extern VOID windivert_cleanup(IN WDFFILEOBJECT object) context_t context = windivert_context_get(object); work_t work; packet_t packet; + WDFQUEUE read_queue; + WDFWORKITEM worker; NTSTATUS status; DEBUG("CLEANUP: cleaning up WinDivert context (context=%p)", context); - if (!windivert_context_verify(context, WINDIVERT_CONTEXT_STATE_OPEN)) + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_OPEN) { +windivert_cleanup_error: + KeReleaseInStackQueuedSpinLock(&lock_handle); + status = STATUS_INVALID_DEVICE_STATE; + DEBUG_ERROR("failed to verify state for cleanup routine", status); return; } - KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); context->state = WINDIVERT_CONTEXT_STATE_CLOSING; while (!IsListEmpty(&context->work_queue)) { @@ -1255,26 +1319,44 @@ extern VOID windivert_cleanup(IN WDFFILEOBJECT object) KeReleaseInStackQueuedSpinLock(&lock_handle); work = CONTAINING_RECORD(entry, struct work_s, entry); FwpsDereferenceNetBufferList(work->buffers, FALSE); - ExFreePoolWithTag(work, WINDIVERT_TAG); + windivert_free(work); KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_CLOSING) + { + goto windivert_cleanup_error; + } } while (!IsListEmpty(&context->packet_queue)) { entry = RemoveHeadList(&context->packet_queue); - KeReleaseInStackQueuedSpinLock(&lock_handle); packet = CONTAINING_RECORD(entry, struct packet_s, entry); + context->packet_queue_length--; + context->packet_queue_size -= packet->data_len; + KeReleaseInStackQueuedSpinLock(&lock_handle); windivert_free_packet(packet); KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_CLOSING) + { + goto windivert_cleanup_error; + } } + read_queue = context->read_queue; KeReleaseInStackQueuedSpinLock(&lock_handle); - WdfIoQueuePurge(context->read_queue, NULL, NULL); - WdfObjectDelete(context->read_queue); + WdfIoQueuePurge(read_queue, NULL, NULL); + WdfObjectDelete(read_queue); for (i = 0; i < WINDIVERT_CONTEXT_MAXWORKERS; i++) { - WdfWorkItemFlush(context->workers[i]); - WdfObjectDelete(context->workers[i]); + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_CLOSING) + { + goto windivert_cleanup_error; + } + worker = context->workers[i]; + KeReleaseInStackQueuedSpinLock(&lock_handle); + WdfWorkItemFlush(worker); + WdfObjectDelete(worker); } - windivert_uninstall_callouts(context); + windivert_uninstall_callouts(context, WINDIVERT_CONTEXT_STATE_CLOSING); FwpmEngineClose0(context->engine_handle); } @@ -1285,14 +1367,18 @@ extern VOID windivert_close(IN WDFFILEOBJECT object) { KLOCK_QUEUE_HANDLE lock_handle; context_t context = windivert_context_get(object); + NTSTATUS status; DEBUG("CLOSE: closing WinDivert context (context=%p)", context); - if (!windivert_context_verify(context, WINDIVERT_CONTEXT_STATE_CLOSING)) + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_CLOSING) { + KeReleaseInStackQueuedSpinLock(&lock_handle); + status = STATUS_INVALID_DEVICE_STATE; + DEBUG_ERROR("failed to verify state for close routine", status); return; } - KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); context->state = WINDIVERT_CONTEXT_STATE_CLOSED; KeReleaseInStackQueuedSpinLock(&lock_handle); } @@ -1302,18 +1388,24 @@ extern VOID windivert_close(IN WDFFILEOBJECT object) */ extern VOID windivert_destroy(IN WDFOBJECT object) { + KLOCK_QUEUE_HANDLE lock_handle; context_t context = windivert_context_get((WDFFILEOBJECT)object); + filter_t filter; + NTSTATUS status; DEBUG("DESTROY: destroying WinDivert context (context=%p)", context); - if (!windivert_context_verify(context, WINDIVERT_CONTEXT_STATE_CLOSED)) + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_CLOSED) { + KeReleaseInStackQueuedSpinLock(&lock_handle); + status = STATUS_INVALID_DEVICE_STATE; + DEBUG_ERROR("failed to verify state for destroy routine", status); return; } - if (context->filter != NULL) - { - ExFreePoolWithTag(context->filter, WINDIVERT_TAG); - } + filter = context->filter; + KeReleaseInStackQueuedSpinLock(&lock_handle); + windivert_free(filter); } /* @@ -1481,12 +1573,15 @@ static void windivert_read_service(context_t context) static NTSTATUS windivert_write(context_t context, WDFREQUEST request, windivert_addr_t addr) { + KLOCK_QUEUE_HANDLE lock_handle; PMDL mdl = NULL, mdl_copy = NULL; PVOID data, data_copy = NULL; UINT data_len; struct iphdr *ip_header; struct ipv6hdr *ipv6_header; BOOL isipv4; + UINT8 layer; + UINT32 priority; HANDLE handle; PNET_BUFFER_LIST buffers = NULL; NTSTATUS status = STATUS_SUCCESS; @@ -1494,12 +1589,6 @@ static NTSTATUS windivert_write(context_t context, WDFREQUEST request, DEBUG("WRITE: writing/injecting a packet (context=%p, request=%p)", context, request); - if (!windivert_context_verify(context, WINDIVERT_CONTEXT_STATE_OPEN)) - { - status = STATUS_INVALID_DEVICE_STATE; - goto windivert_write_exit; - } - if (addr->Direction != WINDIVERT_DIRECTION_INBOUND && addr->Direction != WINDIVERT_DIRECTION_OUTBOUND) { @@ -1532,7 +1621,7 @@ windivert_write_bad_packet: goto windivert_write_exit; } - data_copy = ExAllocatePoolWithTag(NonPagedPool, data_len, WINDIVERT_TAG); + data_copy = windivert_malloc(data_len, FALSE); if (data_copy == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; @@ -1587,25 +1676,35 @@ windivert_write_bad_packet: goto windivert_write_exit; } - handle = (isipv4? inject_handle: injectv6_handle); - if (context->layer == WINDIVERT_LAYER_NETWORK_FORWARD) + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_OPEN) { - status = FwpsInjectForwardAsync0(handle, (HANDLE)context->priority, - 0, (isipv4? AF_INET: AF_INET6), UNSPECIFIED_COMPARTMENT_ID, + KeReleaseInStackQueuedSpinLock(&lock_handle); + status = STATUS_INVALID_DEVICE_STATE; + goto windivert_write_exit; + } + layer = context->layer; + priority = context->priority; + KeReleaseInStackQueuedSpinLock(&lock_handle); + + handle = (isipv4? inject_handle: injectv6_handle); + if (layer == WINDIVERT_LAYER_NETWORK_FORWARD) + { + status = FwpsInjectForwardAsync0(handle, (HANDLE)priority, 0, + (isipv4? AF_INET: AF_INET6), UNSPECIFIED_COMPARTMENT_ID, addr->IfIdx, buffers, windivert_inject_complete, NULL); } else if (addr->Direction == WINDIVERT_DIRECTION_OUTBOUND) { - status = FwpsInjectNetworkSendAsync0(handle, - (HANDLE)context->priority, 0, UNSPECIFIED_COMPARTMENT_ID, buffers, - windivert_inject_complete, NULL); + status = FwpsInjectNetworkSendAsync0(handle, (HANDLE)priority, 0, + UNSPECIFIED_COMPARTMENT_ID, buffers, windivert_inject_complete, + NULL); } else { - status = FwpsInjectNetworkReceiveAsync0(handle, - (HANDLE)context->priority, 0, UNSPECIFIED_COMPARTMENT_ID, - addr->IfIdx, addr->SubIfIdx, buffers, windivert_inject_complete, - NULL); + status = FwpsInjectNetworkReceiveAsync0(handle, (HANDLE)priority, 0, + UNSPECIFIED_COMPARTMENT_ID, addr->IfIdx, addr->SubIfIdx, buffers, + windivert_inject_complete, NULL); } windivert_write_exit: @@ -1625,10 +1724,7 @@ windivert_write_exit: { IoFreeMdl(mdl_copy); } - if (data_copy != NULL) - { - ExFreePoolWithTag(data_copy, WINDIVERT_TAG); - } + windivert_free(data_copy); } return status; @@ -1649,10 +1745,7 @@ static void NTAPI windivert_inject_complete(VOID *context, buffer = NET_BUFFER_LIST_FIRST_NB(buffers); mdl = NET_BUFFER_FIRST_MDL(buffer); data = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority); - if (data != NULL) - { - ExFreePoolWithTag(data, WINDIVERT_TAG); - } + windivert_free(data); IoFreeMdl(mdl); FwpsFreeNetBufferList0(buffers); } @@ -1787,9 +1880,13 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, { KLOCK_QUEUE_HANDLE lock_handle; PCHAR inbuf, outbuf; - size_t inbuflen, outbuflen, filter_len; + size_t inbuflen, outbuflen, filter0_len; windivert_ioctl_t ioctl; - windivert_ioctl_filter_t filter; + windivert_ioctl_filter_t filter0; + filter_t filter; + UINT8 layer; + UINT32 priority; + UINT64 flags; windivert_addr_t addr; req_context_t req_context; NTSTATUS status = STATUS_SUCCESS; @@ -1800,12 +1897,6 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, DEBUG("IOCTL: I/O control request (context=%p)", context); - if (!windivert_context_verify(context, WINDIVERT_CONTEXT_STATE_OPEN)) - { - status = STATUS_INVALID_DEVICE_STATE; - goto windivert_ioctl_exit; - } - // Get the buffers and do sanity checks. status = WdfRequestRetrieveInputBuffer(request, 0, &inbuf, &inbuflen); if (!NT_SUCCESS(status)) @@ -1856,30 +1947,32 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, { BOOL is_inbound, is_outbound, is_ipv4, is_ipv6; - if (InterlockedExchange(&context->filter_on, TRUE) == TRUE) - { - status = STATUS_INVALID_DEVICE_STATE; - DEBUG_ERROR("duplicate START_FILTER ioctl", status); - goto windivert_ioctl_exit; - } - - context->layer = context->layer_0; - context->flags = context->flags_0; - context->priority = context->priority_0; - - filter = (windivert_ioctl_filter_t)outbuf; - filter_len = outbuflen; - context->filter = windivert_filter_compile(filter, filter_len); - if (context->filter == NULL) + filter0 = (windivert_ioctl_filter_t)outbuf; + filter0_len = outbuflen; + filter = windivert_filter_compile(filter0, filter0_len); + if (filter == NULL) { status = STATUS_INVALID_PARAMETER; DEBUG_ERROR("failed to compile filter", status); goto windivert_ioctl_exit; } - windivert_filter_analyze(context->filter, &is_inbound, - &is_outbound, &is_ipv4, &is_ipv6); - status = windivert_install_callouts(context, is_inbound, + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_OPEN || context->on) + { + KeReleaseInStackQueuedSpinLock(&lock_handle); + windivert_free(filter); + status = STATUS_INVALID_DEVICE_STATE; + goto windivert_ioctl_exit; + } + context->on = TRUE; + context->filter = filter; + layer = context->layer; + KeReleaseInStackQueuedSpinLock(&lock_handle); + + windivert_filter_analyze(filter, &is_inbound, &is_outbound, + &is_ipv4, &is_ipv6); + status = windivert_install_callouts(context, layer, is_inbound, is_outbound, is_ipv4, is_ipv6); break; @@ -1893,7 +1986,16 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, DEBUG_ERROR("failed to set layer; value too big", status); goto windivert_ioctl_exit; } - context->layer_0 = (UINT8)ioctl->arg; + layer = (UINT8)ioctl->arg; + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_OPEN || context->on) + { + KeReleaseInStackQueuedSpinLock(&lock_handle); + status = STATUS_INVALID_DEVICE_STATE; + goto windivert_ioctl_exit; + } + context->layer = layer; + KeReleaseInStackQueuedSpinLock(&lock_handle); break; case IOCTL_WINDIVERT_SET_PRIORITY: @@ -1906,8 +2008,16 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, status); goto windivert_ioctl_exit; } - context->priority_0 = - WINDIVERT_CONTEXT_PRIORITY((UINT32)ioctl->arg); + priority = WINDIVERT_CONTEXT_PRIORITY((UINT32)ioctl->arg); + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_OPEN || context->on) + { + KeReleaseInStackQueuedSpinLock(&lock_handle); + status = STATUS_INVALID_DEVICE_STATE; + goto windivert_ioctl_exit; + } + context->priority = priority; + KeReleaseInStackQueuedSpinLock(&lock_handle); break; case IOCTL_WINDIVERT_SET_FLAGS: @@ -1919,7 +2029,16 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, status); goto windivert_ioctl_exit; } - context->flags_0 = ioctl->arg; + flags = ioctl->arg; + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state != WINDIVERT_CONTEXT_STATE_OPEN || context->on) + { + KeReleaseInStackQueuedSpinLock(&lock_handle); + status = STATUS_INVALID_DEVICE_STATE; + goto windivert_ioctl_exit; + } + context->flags = flags; + KeReleaseInStackQueuedSpinLock(&lock_handle); break; case IOCTL_WINDIVERT_SET_PARAM: @@ -1929,7 +2048,7 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, if (context->state != WINDIVERT_CONTEXT_STATE_OPEN) { KeReleaseInStackQueuedSpinLock(&lock_handle); - status = STATUS_INVALID_PARAMETER; + status = STATUS_INVALID_DEVICE_STATE; goto windivert_ioctl_exit; } switch ((WINDIVERT_PARAM)ioctl->arg8) @@ -1999,7 +2118,7 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, if (context->state != WINDIVERT_CONTEXT_STATE_OPEN) { KeReleaseInStackQueuedSpinLock(&lock_handle); - status = STATUS_INVALID_PARAMETER; + status = STATUS_INVALID_DEVICE_STATE; goto windivert_ioctl_exit; } switch ((WINDIVERT_PARAM)ioctl->arg8) @@ -2184,11 +2303,10 @@ static void windivert_classify_callout(context_t context, IN UINT8 direction, PNET_BUFFER buffer, buffer_fst, buffer_itr; NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO checksums_info; UINT8 checksums; - BOOL outbound, queued; - filter_t filter; + BOOL outbound; WDFOBJECT object; work_t work; - ULONG read_queue_len; + filter_t filter; ULONGLONG timestamp; NTSTATUS status; @@ -2244,7 +2362,7 @@ static void windivert_classify_callout(context_t context, IN UINT8 direction, } timestamp = (ULONGLONG)KeQueryPerformanceCounter(NULL).QuadPart; - timestamp--; + timestamp = (timestamp > 0? timestamp-1: timestamp); // Determine which checksum fields are present or not. if (isloopback) @@ -2320,8 +2438,7 @@ static void windivert_classify_callout(context_t context, IN UINT8 direction, // At least one packet matches the filter. Delay all further processing // until windivert_worker() at IRQL=PASSIVE_LEVEL. - work = (work_t)ExAllocatePoolWithTag(NonPagedPool, sizeof(struct work_s), - WINDIVERT_TAG); + work = (work_t)windivert_malloc(sizeof(struct work_s), FALSE); if (work == NULL) { goto windivert_classify_callout_exit; @@ -2338,28 +2455,21 @@ static void windivert_classify_callout(context_t context, IN UINT8 direction, work->sub_if_idx = sub_if_idx; work->priority = priority; work->timestamp = timestamp; - queued = FALSE; KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); - if (context->state == WINDIVERT_CONTEXT_STATE_OPEN) - { - InsertTailList(&context->work_queue, &work->entry); - WdfWorkItemEnqueue(context->workers[context->worker_curr]); - context->worker_curr++; - if (context->worker_curr >= WINDIVERT_CONTEXT_MAXWORKERS) - { - context->worker_curr = 0; - } - queued = TRUE; - } - KeReleaseInStackQueuedSpinLock(&lock_handle); - if (!queued) + if (context->state != WINDIVERT_CONTEXT_STATE_OPEN) { + KeReleaseInStackQueuedSpinLock(&lock_handle); WdfObjectDereference(object); FwpsDereferenceNetBufferList(buffers, FALSE); - ExFreePoolWithTag(work, WINDIVERT_TAG); + windivert_free(work); result->actionType = FWP_ACTION_CONTINUE; return; } + InsertTailList(&context->work_queue, &work->entry); + WdfWorkItemEnqueue(context->workers[context->worker_curr]); + context->worker_curr = + (context->worker_curr + 1) % WINDIVERT_CONTEXT_MAXWORKERS; + KeReleaseInStackQueuedSpinLock(&lock_handle); windivert_classify_callout_exit: @@ -2387,9 +2497,12 @@ VOID windivert_worker(IN WDFWORKITEM item) NTSTATUS status; KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); - sniff_mode = ((context->flags & WINDIVERT_FLAG_SNIFF) != 0); - forward = (context->layer == WINDIVERT_LAYER_NETWORK_FORWARD); - filter = context->filter; + if (context->state == WINDIVERT_CONTEXT_STATE_OPEN) + { + sniff_mode = ((context->flags & WINDIVERT_FLAG_SNIFF) != 0); + forward = (context->layer == WINDIVERT_LAYER_NETWORK_FORWARD); + filter = context->filter; + } while (context->state == WINDIVERT_CONTEXT_STATE_OPEN && !IsListEmpty(&context->work_queue)) { @@ -2483,7 +2596,7 @@ windivert_worker_complete: NdisAdvanceNetBufferDataStart(work->buffer, advance, 0, 0); } FwpsDereferenceNetBufferList(work->buffers, FALSE); - ExFreePoolWithTag(work, WINDIVERT_TAG); + windivert_free(work); KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); } KeReleaseInStackQueuedSpinLock(&lock_handle); @@ -2546,13 +2659,12 @@ static BOOL windivert_queue_packet(context_t context, PNET_BUFFER buffer, // SLOW PATH: queue the packet. data_len = NET_BUFFER_DATA_LENGTH(buffer); - packet = (packet_t)ExAllocatePoolWithTag(NonPagedPool, - sizeof(struct packet_s), WINDIVERT_TAG); + packet = (packet_t)windivert_malloc(sizeof(struct packet_s), FALSE); if (packet == NULL) { return FALSE; } - packet->data = ExAllocatePoolWithTag(PagedPool, data_len, WINDIVERT_TAG); + packet->data = windivert_malloc(data_len, TRUE); if (packet->data == NULL) { windivert_free_packet(packet); @@ -2755,11 +2867,8 @@ static void NTAPI windivert_reinject_clone_complete(VOID *context, */ static void windivert_free_packet(packet_t packet) { - if (packet->data != NULL) - { - ExFreePoolWithTag(packet->data, WINDIVERT_TAG); - } - ExFreePoolWithTag(packet, WINDIVERT_TAG); + windivert_free(packet->data); + windivert_free(packet); } /* @@ -3552,8 +3661,8 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, } // Do NOT use the stack (size = 12Kb on x86) for filter0. - filter0 = (filter_t)ExAllocatePoolWithTag(NonPagedPool, - WINDIVERT_FILTER_MAXLEN*sizeof(struct filter_s), WINDIVERT_TAG); + filter0 = (filter_t)windivert_malloc( + WINDIVERT_FILTER_MAXLEN*sizeof(struct filter_s), TRUE); if (filter0 == NULL) { goto windivert_filter_compile_exit; @@ -3776,8 +3885,7 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, } } - result = (filter_t)ExAllocatePoolWithTag(NonPagedPool, - i*sizeof(struct filter_s), WINDIVERT_TAG); + result = (filter_t)windivert_malloc(i*sizeof(struct filter_s), FALSE); if (result != NULL) { RtlMoveMemory(result, filter0, i*sizeof(struct filter_s)); @@ -3785,10 +3893,7 @@ static filter_t windivert_filter_compile(windivert_ioctl_filter_t ioctl_filter, windivert_filter_compile_exit: - if (filter0 != NULL) - { - ExFreePoolWithTag(filter0, WINDIVERT_TAG); - } + windivert_free(filter0); return result; }