From 156294bb6e657d9ca5ec4d22efb17cb216d533dc Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Fri, 24 May 2019 14:18:18 +0200 Subject: [PATCH] Clear internal NBL queue on transition to MediaConnectStateDisconnected When adapter is in disconnected state, NDIS does not send it any NBLs. After transition to disconnected state it should return all pending NBLs back to NDIS, otherwise a deadlock occurs on pause attempt later. Likewise when the adapter is in low-power state. Signed-off-by: Simon Rozman --- wintun.c | 52 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/wintun.c b/wintun.c index 9718e7b..44ee92b 100644 --- a/wintun.c +++ b/wintun.c @@ -53,6 +53,7 @@ typedef struct _TUN_CTX { volatile TUN_STATE State; volatile NDIS_DEVICE_POWER_STATE PowerState; + EX_SPIN_LOCK TransitionLock; NDIS_HANDLE MiniportAdapterHandle; NDIS_STATISTICS_INFO Statistics; @@ -148,13 +149,15 @@ static void TunCompleteRequest(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp, _In_ ULON _IRQL_requires_same_ _Must_inspect_result_ +_Requires_lock_held_(ctx->TransitionLock) static NTSTATUS TunCheckForPause(_Inout_ TUN_CTX *ctx) { ASSERT(InterlockedGet64(&ctx->ActiveTransactionCount) < MAXLONG64); InterlockedIncrement64(&ctx->ActiveTransactionCount); return - InterlockedGet((LONG *)&ctx->State) != TUN_STATE_RUNNING ? STATUS_NDIS_PAUSED : - InterlockedGet((LONG *)&ctx->PowerState) >= NdisDeviceStateD1 ? STATUS_NDIS_LOW_POWER_STATE : + InterlockedGet64(&ctx->Device.RefCount) <= 0 ? NDIS_STATUS_SEND_ABORTED : + InterlockedGet ((LONG *)&ctx->State) != TUN_STATE_RUNNING ? STATUS_NDIS_PAUSED : + InterlockedGet ((LONG *)&ctx->PowerState) >= NdisDeviceStateD1 ? STATUS_NDIS_LOW_POWER_STATE : STATUS_SUCCESS; } @@ -481,13 +484,13 @@ static void TunQueuePrepend(_Inout_ TUN_CTX *ctx, _In_ NET_BUFFER *nb, _In_ NET_ _Requires_lock_not_held_(ctx->PacketQueue.Lock) _IRQL_requires_max_(DISPATCH_LEVEL) -static void TunQueueClear(_Inout_ TUN_CTX *ctx) +static void TunQueueClear(_Inout_ TUN_CTX *ctx, _In_ NDIS_STATUS status) { KLOCK_QUEUE_HANDLE lqh; KeAcquireInStackQueuedSpinLock(&ctx->PacketQueue.Lock, &lqh); for (NET_BUFFER_LIST *nbl = ctx->PacketQueue.FirstNbl, *nbl_next; nbl; nbl = nbl_next) { nbl_next = NET_BUFFER_LIST_NEXT_NBL(nbl); - NET_BUFFER_LIST_STATUS(nbl) = STATUS_NDIS_PAUSED; + NET_BUFFER_LIST_STATUS(nbl) = status; TunNBLRefDec(ctx, nbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL); } ctx->PacketQueue.FirstNbl = NULL; @@ -569,7 +572,9 @@ _IRQL_requires_max_(DISPATCH_LEVEL) _Must_inspect_result_ static NTSTATUS TunWriteFromIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp) { - NTSTATUS status = STATUS_SUCCESS; + NTSTATUS status; + + KIRQL irql = ExAcquireSpinLockShared(&ctx->TransitionLock); if (!NT_SUCCESS(status = TunCheckForPause(ctx))) goto cleanup_TunCompletePause; @@ -684,6 +689,7 @@ static NTSTATUS TunWriteFromIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp) cleanup_TunCompletePause: TunCompletePause(ctx, TRUE); + ExReleaseSpinLockShared(&ctx->TransitionLock, irql); return status; } @@ -692,6 +698,7 @@ _Use_decl_annotations_ static NTSTATUS TunDispatch(DEVICE_OBJECT *DeviceObject, IRP *Irp) { NTSTATUS status; + KIRQL irql; Irp->IoStatus.Information = 0; @@ -727,18 +734,25 @@ static NTSTATUS TunDispatch(DEVICE_OBJECT *DeviceObject, IRP *Irp) !NT_SUCCESS(status = IoAcquireRemoveLock(&ctx->Device.RemoveLock, Irp))) goto cleanup_complete_req; + irql = ExAcquireSpinLockExclusive(&ctx->TransitionLock); ASSERT(InterlockedGet64(&ctx->Device.RefCount) < MAXLONG64); if (InterlockedIncrement64(&ctx->Device.RefCount) > 0) TunIndicateStatus(ctx->MiniportAdapterHandle, MediaConnectStateConnected); + ExReleaseSpinLockExclusive(&ctx->TransitionLock, irql); IoAcquireRemoveLock(&ctx->Device.RemoveLock, stack->FileObject); status = STATUS_SUCCESS; goto cleanup_complete_req_and_release_remove_lock; case IRP_MJ_CLOSE: + irql = ExAcquireSpinLockExclusive(&ctx->TransitionLock); ASSERT(InterlockedGet64(&ctx->Device.RefCount) > 0); - if (InterlockedDecrement64(&ctx->Device.RefCount) <= 0 && ctx->MiniportAdapterHandle) - TunIndicateStatus(ctx->MiniportAdapterHandle, MediaConnectStateDisconnected); + if (InterlockedDecrement64(&ctx->Device.RefCount) <= 0) { + if (ctx->MiniportAdapterHandle) + TunIndicateStatus(ctx->MiniportAdapterHandle, MediaConnectStateDisconnected); + TunQueueClear(ctx, NDIS_STATUS_SEND_ABORTED); + } + ExReleaseSpinLockExclusive(&ctx->TransitionLock, irql); IoReleaseRemoveLock(&ctx->Device.RemoveLock, stack->FileObject); status = STATUS_SUCCESS; @@ -774,9 +788,10 @@ static NDIS_STATUS TunPause(NDIS_HANDLE MiniportAdapterContext, PNDIS_MINIPORT_P { TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext; + KIRQL irql = ExAcquireSpinLockExclusive(&ctx->TransitionLock); InterlockedExchange((LONG *)&ctx->State, TUN_STATE_PAUSING); - - TunQueueClear(ctx); + ExReleaseSpinLockExclusive(&ctx->TransitionLock, irql); + TunQueueClear(ctx, STATUS_NDIS_PAUSED); return TunCompletePause(ctx, FALSE); } @@ -787,11 +802,12 @@ static NDIS_STATUS TunRestart(NDIS_HANDLE MiniportAdapterContext, PNDIS_MINIPORT { TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext; + KIRQL irql = ExAcquireSpinLockExclusive(&ctx->TransitionLock); InterlockedExchange((LONG *)&ctx->State, TUN_STATE_RESTARTING); - InterlockedExchange64(&ctx->ActiveTransactionCount, 1); - InterlockedExchange((LONG *)&ctx->State, TUN_STATE_RUNNING); + ExReleaseSpinLockExclusive(&ctx->TransitionLock, irql); + return NDIS_STATUS_SUCCESS; } @@ -979,7 +995,7 @@ static NDIS_STATUS TunInitializeEx(NDIS_HANDLE MiniportAdapterHandle, NDIS_HANDL .PoolTag = TUN_MEMORY_TAG }; #pragma warning(suppress: 6014) /* Leaking memory 'ctx->NBLPool'. Note: 'ctx->NBLPool' is freed in TunHaltEx; or freed on failure. */ - ctx->NBLPool = NdisAllocateNetBufferListPool(MiniportAdapterHandle, &nbl_pool_param); + ctx->NBLPool = NdisAllocateNetBufferListPool(MiniportAdapterHandle, &nbl_pool_param); if (!ctx->NBLPool) { status = NDIS_STATUS_FAILURE; goto cleanup_NdisDeregisterDeviceEx; @@ -1158,7 +1174,13 @@ static NDIS_STATUS TunOidSet(_Inout_ TUN_CTX *ctx, _Inout_ NDIS_OID_REQUEST *Oid return NDIS_STATUS_INVALID_LENGTH; } OidRequest->DATA.SET_INFORMATION.BytesRead = sizeof(NDIS_DEVICE_POWER_STATE); - InterlockedExchange((LONG *)&ctx->PowerState, *((NDIS_DEVICE_POWER_STATE *)OidRequest->DATA.SET_INFORMATION.InformationBuffer)); + + KIRQL irql = ExAcquireSpinLockExclusive(&ctx->TransitionLock); + NDIS_DEVICE_POWER_STATE state = *(NDIS_DEVICE_POWER_STATE *)OidRequest->DATA.SET_INFORMATION.InformationBuffer; + if (InterlockedExchange((LONG *)&ctx->PowerState, state) == NdisDeviceStateD0 && state >= NdisDeviceStateD1) + TunQueueClear(ctx, STATUS_NDIS_LOW_POWER_STATE); + ExReleaseSpinLockExclusive(&ctx->TransitionLock, irql); + return NDIS_STATUS_SUCCESS; } @@ -1322,6 +1344,8 @@ static void TunSendNetBufferLists(NDIS_HANDLE MiniportAdapterContext, NET_BUFFER { TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext; + KIRQL irql = ExAcquireSpinLockShared(&ctx->TransitionLock); + NDIS_STATUS status; if (!NT_SUCCESS(status = TunCheckForPause(ctx))) { TunSetNBLStatus(NetBufferLists, status); @@ -1330,10 +1354,12 @@ static void TunSendNetBufferLists(NDIS_HANDLE MiniportAdapterContext, NET_BUFFER } TunQueueAppend(ctx, NetBufferLists, TUN_QUEUE_MAX_NBLS); + TunQueueProcess(ctx); cleanup_TunCompletePause: TunCompletePause(ctx, TRUE); + ExReleaseSpinLockShared(&ctx->TransitionLock, irql); } DRIVER_INITIALIZE DriverEntry;