Manually clean up ugly corners

This commit is contained in:
Jason A. Donenfeld 2019-06-26 17:37:29 +00:00
parent 5ec565c7e8
commit de481cdb12
3 changed files with 199 additions and 196 deletions

View File

@ -91,6 +91,7 @@ StatementMacros: [
'_Acquires_shared_lock_', '_Acquires_shared_lock_',
'_Releases_shared_lock_', '_Releases_shared_lock_',
'_Requires_lock_held_', '_Requires_lock_held_',
'_Requires_lock_not_held_',
'_Use_decl_annotations_', '_Use_decl_annotations_',
'_Guarded_by_', '_Guarded_by_',
'__drv_preferredFunction', '__drv_preferredFunction',

View File

@ -37,6 +37,7 @@ ZwQuerySystemInformation(
PVOID SystemInformation, PVOID SystemInformation,
ULONG SystemInformationLength, ULONG SystemInformationLength,
ULONG *ReturnLength); ULONG *ReturnLength);
extern NDIS_HANDLE extern NDIS_HANDLE
NdisWdfGetAdapterContextFromAdapterHandle(PVOID DeviceExtension); NdisWdfGetAdapterContextFromAdapterHandle(PVOID DeviceExtension);

393
wintun.c
View File

@ -18,6 +18,7 @@
#pragma warning(disable : 4204) // nonstandard extension used: non-constant aggregate initializer #pragma warning(disable : 4204) // nonstandard extension used: non-constant aggregate initializer
#pragma warning(disable : 4221) // nonstandard extension used: <member>: cannot be initialized using address of #pragma warning(disable : 4221) // nonstandard extension used: <member>: cannot be initialized using address of
// automatic variable <variable> // automatic variable <variable>
#pragma warning(disable : 6320) // exception-filter expression is the constant EXCEPTION_EXECUTE_HANDLER
#define NDIS_MINIPORT_VERSION_MIN ((NDIS_MINIPORT_MINIMUM_MAJOR_VERSION << 16) | NDIS_MINIPORT_MINIMUM_MINOR_VERSION) #define NDIS_MINIPORT_VERSION_MIN ((NDIS_MINIPORT_MINIMUM_MAJOR_VERSION << 16) | NDIS_MINIPORT_MINIMUM_MINOR_VERSION)
#define NDIS_MINIPORT_VERSION_MAX ((NDIS_MINIPORT_MAJOR_VERSION << 16) | NDIS_MINIPORT_MINOR_VERSION) #define NDIS_MINIPORT_VERSION_MAX ((NDIS_MINIPORT_MAJOR_VERSION << 16) | NDIS_MINIPORT_MINOR_VERSION)
@ -28,15 +29,15 @@
#define TUN_VENDOR_ID 0xFFFFFF00 #define TUN_VENDOR_ID 0xFFFFFF00
#define TUN_LINK_SPEED 100000000000ULL // 100gbps #define TUN_LINK_SPEED 100000000000ULL // 100gbps
#define TUN_EXCH_MAX_PACKETS \ // Maximum number of full-sized exchange packets that can be exchanged in a single read/write.
256 // Maximum number of full-sized exchange packets that can be exchanged in a single read/write #define TUN_EXCH_MAX_PACKETS 256
#define TUN_EXCH_MAX_PACKET_SIZE \ // Maximum exchange packet size - empirically determined by net buffer list (pool) limitations
0xF000 // Maximum exchange packet size - empirically determined by net buffer list (pool) limitations #define TUN_EXCH_MAX_PACKET_SIZE 0xF000
#define TUN_EXCH_ALIGNMENT 16 // Memory alignment in exchange buffers #define TUN_EXCH_ALIGNMENT 16 // Memory alignment in exchange buffers
#define TUN_EXCH_MAX_IP_PACKET_SIZE \ // Maximum IP packet size (headers + payload)
(TUN_EXCH_MAX_PACKET_SIZE - sizeof(TUN_PACKET)) // Maximum IP packet size (headers + payload) #define TUN_EXCH_MAX_IP_PACKET_SIZE (TUN_EXCH_MAX_PACKET_SIZE - sizeof(TUN_PACKET))
#define TUN_EXCH_MAX_BUFFER_SIZE \ // Maximum size of read/write exchange buffer
(TUN_EXCH_MAX_PACKETS * TUN_EXCH_MAX_PACKET_SIZE) // Maximum size of read/write exchange buffer #define TUN_EXCH_MAX_BUFFER_SIZE (TUN_EXCH_MAX_PACKETS * TUN_EXCH_MAX_PACKET_SIZE)
#define TUN_EXCH_MIN_BUFFER_SIZE_READ TUN_EXCH_MAX_PACKET_SIZE // Minimum size of read exchange buffer #define TUN_EXCH_MIN_BUFFER_SIZE_READ TUN_EXCH_MAX_PACKET_SIZE // Minimum size of read exchange buffer
#define TUN_EXCH_MIN_BUFFER_SIZE_WRITE (sizeof(TUN_PACKET)) // Minimum size of write exchange buffer #define TUN_EXCH_MIN_BUFFER_SIZE_WRITE (sizeof(TUN_PACKET)) // Minimum size of write exchange buffer
#define TUN_QUEUE_MAX_NBLS 1000 #define TUN_QUEUE_MAX_NBLS 1000
@ -48,10 +49,10 @@
# define TUN_HTONS(x) ((USHORT)(x)) # define TUN_HTONS(x) ((USHORT)(x))
# define TUN_HTONL(x) ((ULONG)(x)) # define TUN_HTONL(x) ((ULONG)(x))
#elif REG_DWORD == REG_DWORD_LITTLE_ENDIAN #elif REG_DWORD == REG_DWORD_LITTLE_ENDIAN
# define TUN_HTONS(x) (((USHORT)(x)&0x00ff) << 8 | ((USHORT)(x)&0xff00) >> 8) # define TUN_HTONS(x) ((((USHORT)(x)&0x00ff) << 8) | (((USHORT)(x)&0xff00) >> 8))
# define TUN_HTONL(x) \ # define TUN_HTONL(x) \
(((ULONG)(x)&0x000000ff) << 24 | ((ULONG)(x)&0x0000ff00) << 8 | ((ULONG)(x)&0x00ff0000) >> 8 | \ ((((ULONG)(x)&0x000000ff) << 24) | (((ULONG)(x)&0x0000ff00) << 8) | (((ULONG)(x)&0x00ff0000) >> 8) | \
((ULONG)(x)&0xff000000) >> 24) (((ULONG)(x)&0xff000000) >> 24))
#else #else
# error "Unable to determine endianess" # error "Unable to determine endianess"
#endif #endif
@ -77,8 +78,7 @@ typedef struct _TUN_CTX
* atomic and then releasing. It's similar to setting the atomic and then calling rcu_barrier(). */ * atomic and then releasing. It's similar to setting the atomic and then calling rcu_barrier(). */
EX_SPIN_LOCK TransitionLock; EX_SPIN_LOCK TransitionLock;
/* This is actually a pointer to NDIS_MINIPORT_BLOCK struct. */ NDIS_HANDLE MiniportAdapterHandle; // This is actually a pointer to NDIS_MINIPORT_BLOCK struct.
NDIS_HANDLE MiniportAdapterHandle;
NDIS_STATISTICS_INFO Statistics; NDIS_STATISTICS_INFO Statistics;
volatile LONG64 ActiveNBLCount; volatile LONG64 ActiveNBLCount;
@ -166,22 +166,22 @@ TunIndicateStatus(_In_ NDIS_HANDLE MiniportAdapterHandle, _In_ NDIS_MEDIA_CONNEC
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
static void static void
TunCompleteRequest(_Inout_ TUN_CTX *ctx, _Inout_ IRP *irp, _In_ NTSTATUS status, _In_ CCHAR priority_boost) TunCompleteRequest(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp, _In_ NTSTATUS Status, _In_ CCHAR PriorityBoost)
{ {
irp->IoStatus.Status = status; Irp->IoStatus.Status = Status;
IoCompleteRequest(irp, priority_boost); IoCompleteRequest(Irp, PriorityBoost);
IoReleaseRemoveLock(&ctx->Device.RemoveLock, irp); IoReleaseRemoveLock(&Ctx->Device.RemoveLock, Irp);
} }
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
static NDIS_STATUS static NDIS_STATUS
TunCompletePause(_Inout_ TUN_CTX *ctx, _In_ BOOLEAN async_completion) TunCompletePause(_Inout_ TUN_CTX *Ctx, _In_ BOOLEAN AsyncCompletion)
{ {
ASSERT(InterlockedGet64(&ctx->ActiveNBLCount) > 0); ASSERT(InterlockedGet64(&Ctx->ActiveNBLCount) > 0);
if (InterlockedDecrement64(&ctx->ActiveNBLCount) <= 0) if (InterlockedDecrement64(&Ctx->ActiveNBLCount) <= 0)
{ {
if (async_completion) if (AsyncCompletion)
NdisMPauseComplete(ctx->MiniportAdapterHandle); NdisMPauseComplete(Ctx->MiniportAdapterHandle);
return NDIS_STATUS_SUCCESS; return NDIS_STATUS_SUCCESS;
} }
@ -236,7 +236,7 @@ TunCsqPeekNextIrp(IO_CSQ *Csq, IRP *Irp, _In_ PVOID PeekContext)
_IRQL_raises_(DISPATCH_LEVEL) _IRQL_raises_(DISPATCH_LEVEL)
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
_Requires_lock_not_held_(CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq)->Device.ReadQueue.Lock) _Requires_lock_not_held_(CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq)->Device.ReadQueue.Lock)
_Acquires_lock_(CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq)->Device.ReadQueue.Lock) _Acquires_lock_(CONTAINING_RECORD(Csq, TUN_CTX, Device.ReadQueue.Csq)->Device.ReadQueue.Lock)
static VOID static VOID
TunCsqAcquireLock(_In_ IO_CSQ *Csq, _Out_ _At_(*Irql, _Post_ _IRQL_saves_) KIRQL *Irql) TunCsqAcquireLock(_In_ IO_CSQ *Csq, _Out_ _At_(*Irql, _Post_ _IRQL_saves_) KIRQL *Irql)
{ {
@ -264,27 +264,26 @@ TunCsqCompleteCanceledIrp(IO_CSQ *Csq, IRP *Irp)
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_ _Must_inspect_result_
static NTSTATUS static NTSTATUS
TunGetIrpBuffer(_In_ IRP *Irp, _Out_ UCHAR **buffer, _Out_ ULONG *size) TunGetIrpBuffer(_In_ IRP *Irp, _Out_ UCHAR **buffer, _Out_ ULONG *Size)
{ {
TUN_MAPPED_UBUFFER *ubuffer = NULL; TUN_MAPPED_UBUFFER *ubuffer;
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(Irp); IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(Irp);
TUN_FILE_CTX *file_ctx = (TUN_FILE_CTX *)stack->FileObject->FsContext; TUN_FILE_CTX *file_ctx = (TUN_FILE_CTX *)stack->FileObject->FsContext;
switch (stack->MajorFunction) switch (stack->MajorFunction)
{ {
case IRP_MJ_READ: case IRP_MJ_READ:
*size = stack->Parameters.Read.Length; *Size = stack->Parameters.Read.Length;
ubuffer = &file_ctx->ReadBuffer; ubuffer = &file_ctx->ReadBuffer;
break; break;
case IRP_MJ_WRITE: case IRP_MJ_WRITE:
*size = stack->Parameters.Write.Length; *Size = stack->Parameters.Write.Length;
ubuffer = &file_ctx->WriteBuffer; ubuffer = &file_ctx->WriteBuffer;
break; break;
default: default:
ASSERT(FALSE); return STATUS_INVALID_PARAMETER;
} }
_Analysis_assume_(ubuffer != NULL); if (*Size > ubuffer->Size)
if (*size > ubuffer->Size)
return STATUS_INVALID_USER_BUFFER; return STATUS_INVALID_USER_BUFFER;
ASSERT(ubuffer->KernelAddress != NULL); ASSERT(ubuffer->KernelAddress != NULL);
*buffer = ubuffer->KernelAddress; *buffer = ubuffer->KernelAddress;
@ -302,7 +301,7 @@ TunMapUbuffer(_Inout_ TUN_MAPPED_UBUFFER *MappedBuffer, _In_ VOID *UserAddress,
return STATUS_SUCCESS; return STATUS_SUCCESS;
return STATUS_ALREADY_INITIALIZED; return STATUS_ALREADY_INITIALIZED;
} }
__try try
{ {
ProbeForWrite(UserAddress, Size, 1); ProbeForWrite(UserAddress, Size, 1);
ProbeForRead(UserAddress, Size, 1); ProbeForRead(UserAddress, Size, 1);
@ -322,7 +321,7 @@ TunMapUbuffer(_Inout_ TUN_MAPPED_UBUFFER *MappedBuffer, _In_ VOID *UserAddress,
MappedBuffer->UserAddress = UserAddress; MappedBuffer->UserAddress = UserAddress;
MappedBuffer->Size = Size; MappedBuffer->Size = Size;
} }
__except (EXCEPTION_EXECUTE_HANDLER) except(EXCEPTION_EXECUTE_HANDLER)
{ {
if (MappedBuffer->Mdl) if (MappedBuffer->Mdl)
{ {
@ -381,48 +380,48 @@ TunMapIrp(_In_ IRP *Irp)
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_ _Must_inspect_result_
static _Return_type_success_( static _Return_type_success_(
return != NULL) IRP *TunRemoveNextIrp(_Inout_ TUN_CTX *ctx, _Out_ UCHAR **buffer, _Out_ ULONG *size) return != NULL) IRP *TunRemoveNextIrp(_Inout_ TUN_CTX *Ctx, _Out_ UCHAR **Buffer, _Out_ ULONG *Size)
{ {
IRP *irp; IRP *irp;
retry: retry:
irp = IoCsqRemoveNextIrp(&ctx->Device.ReadQueue.Csq, NULL); irp = IoCsqRemoveNextIrp(&Ctx->Device.ReadQueue.Csq, NULL);
if (!irp) if (!irp)
return NULL; return NULL;
NTSTATUS status = TunGetIrpBuffer(irp, buffer, size); NTSTATUS status = TunGetIrpBuffer(irp, Buffer, Size);
if (!NT_SUCCESS(status)) if (!NT_SUCCESS(status))
{ {
TunCompleteRequest(ctx, irp, status, IO_NO_INCREMENT); TunCompleteRequest(Ctx, irp, status, IO_NO_INCREMENT);
goto retry; goto retry;
} }
ASSERT(irp->IoStatus.Information <= (ULONG_PTR)*size); ASSERT(irp->IoStatus.Information <= (ULONG_PTR)*Size);
return irp; return irp;
} }
_IRQL_requires_same_ static BOOLEAN _IRQL_requires_same_ static BOOLEAN
TunWontFitIntoIrp(_In_ IRP *Irp, _In_ ULONG size, _In_ NET_BUFFER *nb) TunWontFitIntoIrp(_In_ IRP *Irp, _In_ ULONG Size, _In_ NET_BUFFER *Nb)
{ {
return (ULONG_PTR)size < return (ULONG_PTR)Size <
Irp->IoStatus.Information + TunPacketAlign(sizeof(TUN_PACKET) + NET_BUFFER_DATA_LENGTH(nb)); Irp->IoStatus.Information + TunPacketAlign(sizeof(TUN_PACKET) + NET_BUFFER_DATA_LENGTH(Nb));
} }
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_ _Must_inspect_result_
static NTSTATUS static NTSTATUS
TunWriteIntoIrp(_Inout_ IRP *Irp, _Inout_ UCHAR *buffer, _In_ NET_BUFFER *nb, _Inout_ NDIS_STATISTICS_INFO *statistics) TunWriteIntoIrp(_Inout_ IRP *Irp, _Inout_ UCHAR *Buffer, _In_ NET_BUFFER *Nb, _Inout_ NDIS_STATISTICS_INFO *Statistics)
{ {
ULONG p_size = NET_BUFFER_DATA_LENGTH(nb); ULONG p_size = NET_BUFFER_DATA_LENGTH(Nb);
TUN_PACKET *p = (TUN_PACKET *)(buffer + Irp->IoStatus.Information); TUN_PACKET *p = (TUN_PACKET *)(Buffer + Irp->IoStatus.Information);
p->Size = p_size; p->Size = p_size;
void *ptr = NdisGetDataBuffer(nb, p_size, p->Data, 1, 0); void *ptr = NdisGetDataBuffer(Nb, p_size, p->Data, 1, 0);
if (!ptr) if (!ptr)
{ {
if (statistics) if (Statistics)
InterlockedIncrement64((LONG64 *)&statistics->ifOutErrors); InterlockedIncrement64((LONG64 *)&Statistics->ifOutErrors);
return NDIS_STATUS_RESOURCES; return NDIS_STATUS_RESOURCES;
} }
if (ptr != p->Data) if (ptr != p->Data)
@ -430,114 +429,115 @@ TunWriteIntoIrp(_Inout_ IRP *Irp, _Inout_ UCHAR *buffer, _In_ NET_BUFFER *nb, _I
Irp->IoStatus.Information += TunPacketAlign(sizeof(TUN_PACKET) + p_size); Irp->IoStatus.Information += TunPacketAlign(sizeof(TUN_PACKET) + p_size);
InterlockedAdd64((LONG64 *)&statistics->ifHCOutOctets, p_size); InterlockedAdd64((LONG64 *)&Statistics->ifHCOutOctets, p_size);
InterlockedAdd64((LONG64 *)&statistics->ifHCOutUcastOctets, p_size); InterlockedAdd64((LONG64 *)&Statistics->ifHCOutUcastOctets, p_size);
InterlockedIncrement64((LONG64 *)&statistics->ifHCOutUcastPkts); InterlockedIncrement64((LONG64 *)&Statistics->ifHCOutUcastPkts);
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
#define NET_BUFFER_LIST_REFCOUNT(nbl) ((volatile LONG64 *)NET_BUFFER_LIST_MINIPORT_RESERVED(nbl)) #define NET_BUFFER_LIST_REFCOUNT(nbl) ((volatile LONG64 *)NET_BUFFER_LIST_MINIPORT_RESERVED(nbl))
_IRQL_requires_same_ static void _IRQL_requires_same_ static void
TunNBLRefInit(_Inout_ TUN_CTX *ctx, _Inout_ NET_BUFFER_LIST *nbl) TunNBLRefInit(_Inout_ TUN_CTX *Ctx, _Inout_ NET_BUFFER_LIST *Nbl)
{ {
InterlockedIncrement64(&ctx->ActiveNBLCount); InterlockedIncrement64(&Ctx->ActiveNBLCount);
InterlockedIncrement(&ctx->PacketQueue.NumNbl); InterlockedIncrement(&Ctx->PacketQueue.NumNbl);
InterlockedExchange64(NET_BUFFER_LIST_REFCOUNT(nbl), 1); InterlockedExchange64(NET_BUFFER_LIST_REFCOUNT(Nbl), 1);
} }
_IRQL_requires_same_ static void _IRQL_requires_same_ static void
TunNBLRefInc(_Inout_ NET_BUFFER_LIST *nbl) TunNBLRefInc(_Inout_ NET_BUFFER_LIST *Nbl)
{ {
ASSERT(InterlockedGet64(NET_BUFFER_LIST_REFCOUNT(nbl))); ASSERT(InterlockedGet64(NET_BUFFER_LIST_REFCOUNT(Nbl)));
InterlockedIncrement64(NET_BUFFER_LIST_REFCOUNT(nbl)); InterlockedIncrement64(NET_BUFFER_LIST_REFCOUNT(Nbl));
} }
_When_((SendCompleteFlags & NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL), _IRQL_requires_(DISPATCH_LEVEL)) _When_((SendCompleteFlags & NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL), _IRQL_requires_(DISPATCH_LEVEL))
_When_(!(SendCompleteFlags & NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL), _IRQL_requires_max_(DISPATCH_LEVEL)) _When_(!(SendCompleteFlags & NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL), _IRQL_requires_max_(DISPATCH_LEVEL))
static BOOLEAN static BOOLEAN
TunNBLRefDec(_Inout_ TUN_CTX *ctx, _Inout_ NET_BUFFER_LIST *nbl, _In_ ULONG SendCompleteFlags) TunNBLRefDec(_Inout_ TUN_CTX *Ctx, _Inout_ NET_BUFFER_LIST *Nbl, _In_ ULONG SendCompleteFlags)
{ {
ASSERT(InterlockedGet64(NET_BUFFER_LIST_REFCOUNT(nbl)) > 0); ASSERT(InterlockedGet64(NET_BUFFER_LIST_REFCOUNT(Nbl)) > 0);
if (InterlockedDecrement64(NET_BUFFER_LIST_REFCOUNT(nbl)) <= 0) if (InterlockedDecrement64(NET_BUFFER_LIST_REFCOUNT(Nbl)) <= 0)
{ {
NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL; NET_BUFFER_LIST_NEXT_NBL(Nbl) = NULL;
NdisMSendNetBufferListsComplete(ctx->MiniportAdapterHandle, nbl, SendCompleteFlags); NdisMSendNetBufferListsComplete(Ctx->MiniportAdapterHandle, Nbl, SendCompleteFlags);
ASSERT(InterlockedGet(&ctx->PacketQueue.NumNbl) > 0); ASSERT(InterlockedGet(&Ctx->PacketQueue.NumNbl) > 0);
InterlockedDecrement(&ctx->PacketQueue.NumNbl); InterlockedDecrement(&Ctx->PacketQueue.NumNbl);
TunCompletePause(ctx, TRUE); TunCompletePause(Ctx, TRUE);
return TRUE; return TRUE;
} }
return FALSE; return FALSE;
} }
_IRQL_requires_same_ static void _IRQL_requires_same_ static void
TunAppendNBL(_Inout_ NET_BUFFER_LIST **head, _Inout_ NET_BUFFER_LIST **tail, __drv_aliasesMem _In_ NET_BUFFER_LIST *nbl) TunAppendNBL(_Inout_ NET_BUFFER_LIST **Head, _Inout_ NET_BUFFER_LIST **Tail, __drv_aliasesMem _In_ NET_BUFFER_LIST *Nbl)
{ {
*(*tail ? &NET_BUFFER_LIST_NEXT_NBL(*tail) : head) = nbl; *(*Tail ? &NET_BUFFER_LIST_NEXT_NBL(*Tail) : Head) = Nbl;
*tail = nbl; *Tail = Nbl;
NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL; NET_BUFFER_LIST_NEXT_NBL(Nbl) = NULL;
} }
_Requires_lock_not_held_(ctx->PacketQueue.Lock) _IRQL_requires_max_(DISPATCH_LEVEL) _Requires_lock_not_held_(Ctx->PacketQueue.Lock)
_IRQL_requires_max_(DISPATCH_LEVEL)
static void static void
TunQueueAppend(_Inout_ TUN_CTX *ctx, _In_ NET_BUFFER_LIST *nbl, _In_ UINT max_nbls) TunQueueAppend(_Inout_ TUN_CTX *Ctx, _In_ NET_BUFFER_LIST *Nbl, _In_ UINT MaxNbls)
{ {
for (NET_BUFFER_LIST *nbl_next; nbl; nbl = nbl_next) for (NET_BUFFER_LIST *nbl_next; Nbl; Nbl = nbl_next)
{ {
nbl_next = NET_BUFFER_LIST_NEXT_NBL(nbl); nbl_next = NET_BUFFER_LIST_NEXT_NBL(Nbl);
if (!NET_BUFFER_LIST_FIRST_NB(nbl)) if (!NET_BUFFER_LIST_FIRST_NB(Nbl))
{ {
NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL; NET_BUFFER_LIST_NEXT_NBL(Nbl) = NULL;
NdisMSendNetBufferListsComplete(ctx->MiniportAdapterHandle, nbl, 0); NdisMSendNetBufferListsComplete(Ctx->MiniportAdapterHandle, Nbl, 0);
continue; continue;
} }
KLOCK_QUEUE_HANDLE lqh; KLOCK_QUEUE_HANDLE lqh;
KeAcquireInStackQueuedSpinLock(&ctx->PacketQueue.Lock, &lqh); KeAcquireInStackQueuedSpinLock(&Ctx->PacketQueue.Lock, &lqh);
TunNBLRefInit(ctx, nbl); TunNBLRefInit(Ctx, Nbl);
TunAppendNBL(&ctx->PacketQueue.FirstNbl, &ctx->PacketQueue.LastNbl, nbl); TunAppendNBL(&Ctx->PacketQueue.FirstNbl, &Ctx->PacketQueue.LastNbl, Nbl);
while ((UINT)InterlockedGet(&ctx->PacketQueue.NumNbl) > max_nbls && ctx->PacketQueue.FirstNbl) while ((UINT)InterlockedGet(&Ctx->PacketQueue.NumNbl) > MaxNbls && Ctx->PacketQueue.FirstNbl)
{ {
NET_BUFFER_LIST *nbl_second = NET_BUFFER_LIST_NEXT_NBL(ctx->PacketQueue.FirstNbl); NET_BUFFER_LIST *nbl_second = NET_BUFFER_LIST_NEXT_NBL(Ctx->PacketQueue.FirstNbl);
NET_BUFFER_LIST_STATUS(ctx->PacketQueue.FirstNbl) = NDIS_STATUS_SEND_ABORTED; NET_BUFFER_LIST_STATUS(Ctx->PacketQueue.FirstNbl) = NDIS_STATUS_SEND_ABORTED;
TunNBLRefDec(ctx, ctx->PacketQueue.FirstNbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL); TunNBLRefDec(Ctx, Ctx->PacketQueue.FirstNbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
ctx->PacketQueue.NextNb = NULL; Ctx->PacketQueue.NextNb = NULL;
ctx->PacketQueue.FirstNbl = nbl_second; Ctx->PacketQueue.FirstNbl = nbl_second;
if (!ctx->PacketQueue.FirstNbl) if (!Ctx->PacketQueue.FirstNbl)
ctx->PacketQueue.LastNbl = NULL; Ctx->PacketQueue.LastNbl = NULL;
} }
KeReleaseInStackQueuedSpinLock(&lqh); KeReleaseInStackQueuedSpinLock(&lqh);
} }
} }
_Requires_lock_held_(ctx->PacketQueue.Lock) _Requires_lock_held_(Ctx->PacketQueue.Lock)
_IRQL_requires_(DISPATCH_LEVEL) _IRQL_requires_(DISPATCH_LEVEL)
_Must_inspect_result_ _Must_inspect_result_
static _Return_type_success_(return != static _Return_type_success_(return !=
NULL) NET_BUFFER *TunQueueRemove(_Inout_ TUN_CTX *ctx, _Out_ NET_BUFFER_LIST **nbl) NULL) NET_BUFFER *TunQueueRemove(_Inout_ TUN_CTX *Ctx, _Out_ NET_BUFFER_LIST **Nbl)
{ {
NET_BUFFER_LIST *nbl_top; NET_BUFFER_LIST *nbl_top;
NET_BUFFER *ret; NET_BUFFER *ret;
retry: retry:
nbl_top = ctx->PacketQueue.FirstNbl; nbl_top = Ctx->PacketQueue.FirstNbl;
*nbl = nbl_top; *Nbl = nbl_top;
if (!nbl_top) if (!nbl_top)
return NULL; return NULL;
if (!ctx->PacketQueue.NextNb) if (!Ctx->PacketQueue.NextNb)
ctx->PacketQueue.NextNb = NET_BUFFER_LIST_FIRST_NB(nbl_top); Ctx->PacketQueue.NextNb = NET_BUFFER_LIST_FIRST_NB(nbl_top);
ret = ctx->PacketQueue.NextNb; ret = Ctx->PacketQueue.NextNb;
ctx->PacketQueue.NextNb = NET_BUFFER_NEXT_NB(ret); Ctx->PacketQueue.NextNb = NET_BUFFER_NEXT_NB(ret);
if (!ctx->PacketQueue.NextNb) if (!Ctx->PacketQueue.NextNb)
{ {
ctx->PacketQueue.FirstNbl = NET_BUFFER_LIST_NEXT_NBL(nbl_top); Ctx->PacketQueue.FirstNbl = NET_BUFFER_LIST_NEXT_NBL(nbl_top);
if (!ctx->PacketQueue.FirstNbl) if (!Ctx->PacketQueue.FirstNbl)
ctx->PacketQueue.LastNbl = NULL; Ctx->PacketQueue.LastNbl = NULL;
NET_BUFFER_LIST_NEXT_NBL(nbl_top) = NULL; NET_BUFFER_LIST_NEXT_NBL(nbl_top) = NULL;
} }
else else
@ -546,57 +546,59 @@ retry:
if (ret && NET_BUFFER_DATA_LENGTH(ret) > TUN_EXCH_MAX_IP_PACKET_SIZE) if (ret && NET_BUFFER_DATA_LENGTH(ret) > TUN_EXCH_MAX_IP_PACKET_SIZE)
{ {
NET_BUFFER_LIST_STATUS(nbl_top) = NDIS_STATUS_INVALID_LENGTH; NET_BUFFER_LIST_STATUS(nbl_top) = NDIS_STATUS_INVALID_LENGTH;
TunNBLRefDec(ctx, nbl_top, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL); TunNBLRefDec(Ctx, nbl_top, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
InterlockedIncrement64((LONG64 *)&ctx->Statistics.ifOutDiscards); InterlockedIncrement64((LONG64 *)&Ctx->Statistics.ifOutDiscards);
goto retry; /* A for (;;) and a break would be fine, but this is clearer actually. */ goto retry;
} }
return ret; return ret;
} }
/* Note: Must be called immediately after TunQueueRemove without dropping ctx->PacketQueue.Lock. */ // Note: Must be called immediately after TunQueueRemove without dropping ctx->PacketQueue.Lock.
_Requires_lock_held_(ctx->PacketQueue.Lock) _Requires_lock_held_(Ctx->PacketQueue.Lock)
_IRQL_requires_(DISPATCH_LEVEL) _IRQL_requires_(DISPATCH_LEVEL)
static void static void
TunQueuePrepend(_Inout_ TUN_CTX *ctx, _In_ NET_BUFFER *nb, _In_ NET_BUFFER_LIST *nbl) TunQueuePrepend(_Inout_ TUN_CTX *Ctx, _In_ NET_BUFFER *Nb, _In_ NET_BUFFER_LIST *Nbl)
{ {
ctx->PacketQueue.NextNb = nb; Ctx->PacketQueue.NextNb = Nb;
if (!nbl || nbl == ctx->PacketQueue.FirstNbl) if (!Nbl || Nbl == Ctx->PacketQueue.FirstNbl)
return; return;
TunNBLRefInc(nbl); TunNBLRefInc(Nbl);
if (!ctx->PacketQueue.FirstNbl) if (!Ctx->PacketQueue.FirstNbl)
ctx->PacketQueue.FirstNbl = ctx->PacketQueue.LastNbl = nbl; Ctx->PacketQueue.FirstNbl = Ctx->PacketQueue.LastNbl = Nbl;
else else
{ {
NET_BUFFER_LIST_NEXT_NBL(nbl) = ctx->PacketQueue.FirstNbl; NET_BUFFER_LIST_NEXT_NBL(Nbl) = Ctx->PacketQueue.FirstNbl;
ctx->PacketQueue.FirstNbl = nbl; Ctx->PacketQueue.FirstNbl = Nbl;
} }
} }
_Requires_lock_not_held_(ctx->PacketQueue.Lock) _IRQL_requires_max_(DISPATCH_LEVEL) _Requires_lock_not_held_(Ctx->PacketQueue.Lock)
_IRQL_requires_max_(DISPATCH_LEVEL)
static void static void
TunQueueClear(_Inout_ TUN_CTX *ctx, _In_ NDIS_STATUS status) TunQueueClear(_Inout_ TUN_CTX *Ctx, _In_ NDIS_STATUS Status)
{ {
KLOCK_QUEUE_HANDLE lqh; KLOCK_QUEUE_HANDLE lqh;
KeAcquireInStackQueuedSpinLock(&ctx->PacketQueue.Lock, &lqh); KeAcquireInStackQueuedSpinLock(&Ctx->PacketQueue.Lock, &lqh);
for (NET_BUFFER_LIST *nbl = ctx->PacketQueue.FirstNbl, *nbl_next; nbl; nbl = nbl_next) for (NET_BUFFER_LIST *nbl = Ctx->PacketQueue.FirstNbl, *nbl_next; nbl; nbl = nbl_next)
{ {
nbl_next = NET_BUFFER_LIST_NEXT_NBL(nbl); nbl_next = NET_BUFFER_LIST_NEXT_NBL(nbl);
NET_BUFFER_LIST_STATUS(nbl) = status; NET_BUFFER_LIST_STATUS(nbl) = Status;
TunNBLRefDec(ctx, nbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL); TunNBLRefDec(Ctx, nbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
} }
ctx->PacketQueue.FirstNbl = NULL; Ctx->PacketQueue.FirstNbl = NULL;
ctx->PacketQueue.LastNbl = NULL; Ctx->PacketQueue.LastNbl = NULL;
ctx->PacketQueue.NextNb = NULL; Ctx->PacketQueue.NextNb = NULL;
InterlockedExchange(&ctx->PacketQueue.NumNbl, 0); InterlockedExchange(&Ctx->PacketQueue.NumNbl, 0);
KeReleaseInStackQueuedSpinLock(&lqh); KeReleaseInStackQueuedSpinLock(&lqh);
} }
_Requires_lock_not_held_(ctx->PacketQueue.Lock) _IRQL_requires_max_(DISPATCH_LEVEL) _Requires_lock_not_held_(Ctx->PacketQueue.Lock)
_IRQL_requires_max_(DISPATCH_LEVEL)
static void static void
TunQueueProcess(_Inout_ TUN_CTX *ctx) TunQueueProcess(_Inout_ TUN_CTX *Ctx)
{ {
IRP *irp = NULL; IRP *irp = NULL;
UCHAR *buffer = NULL; UCHAR *buffer = NULL;
@ -608,24 +610,24 @@ TunQueueProcess(_Inout_ TUN_CTX *ctx)
{ {
NET_BUFFER_LIST *nbl; NET_BUFFER_LIST *nbl;
KeAcquireInStackQueuedSpinLock(&ctx->PacketQueue.Lock, &lqh); KeAcquireInStackQueuedSpinLock(&Ctx->PacketQueue.Lock, &lqh);
/* Get head NB (and IRP). */ /* Get head NB (and IRP). */
if (!irp) if (!irp)
{ {
nb = TunQueueRemove(ctx, &nbl); nb = TunQueueRemove(Ctx, &nbl);
if (!nb) if (!nb)
{ {
KeReleaseInStackQueuedSpinLock(&lqh); KeReleaseInStackQueuedSpinLock(&lqh);
return; return;
} }
irp = TunRemoveNextIrp(ctx, &buffer, &size); irp = TunRemoveNextIrp(Ctx, &buffer, &size);
if (!irp) if (!irp)
{ {
TunQueuePrepend(ctx, nb, nbl); TunQueuePrepend(Ctx, nb, nbl);
KeReleaseInStackQueuedSpinLock(&lqh); KeReleaseInStackQueuedSpinLock(&lqh);
if (nbl) if (nbl)
TunNBLRefDec(ctx, nbl, 0); TunNBLRefDec(Ctx, nbl, 0);
return; return;
} }
@ -633,14 +635,14 @@ TunQueueProcess(_Inout_ TUN_CTX *ctx)
_Analysis_assume_(irp->IoStatus.Information <= size); _Analysis_assume_(irp->IoStatus.Information <= size);
} }
else else
nb = TunQueueRemove(ctx, &nbl); nb = TunQueueRemove(Ctx, &nbl);
/* If the NB won't fit in the IRP, return it. */ /* If the NB won't fit in the IRP, return it. */
if (nb && TunWontFitIntoIrp(irp, size, nb)) if (nb && TunWontFitIntoIrp(irp, size, nb))
{ {
TunQueuePrepend(ctx, nb, nbl); TunQueuePrepend(Ctx, nb, nbl);
if (nbl) if (nbl)
TunNBLRefDec(ctx, nbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL); TunNBLRefDec(Ctx, nbl, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
nbl = NULL; nbl = NULL;
nb = NULL; nb = NULL;
} }
@ -650,31 +652,31 @@ TunQueueProcess(_Inout_ TUN_CTX *ctx)
/* Process NB and IRP. */ /* Process NB and IRP. */
if (nb) if (nb)
{ {
NTSTATUS status = TunWriteIntoIrp(irp, buffer, nb, &ctx->Statistics); NTSTATUS status = TunWriteIntoIrp(irp, buffer, nb, &Ctx->Statistics);
if (!NT_SUCCESS(status)) if (!NT_SUCCESS(status))
{ {
if (nbl) if (nbl)
NET_BUFFER_LIST_STATUS(nbl) = status; NET_BUFFER_LIST_STATUS(nbl) = status;
IoCsqInsertIrpEx(&ctx->Device.ReadQueue.Csq, irp, NULL, TUN_CSQ_INSERT_HEAD); IoCsqInsertIrpEx(&Ctx->Device.ReadQueue.Csq, irp, NULL, TUN_CSQ_INSERT_HEAD);
irp = NULL; irp = NULL;
} }
} }
else else
{ {
TunCompleteRequest(ctx, irp, STATUS_SUCCESS, IO_NETWORK_INCREMENT); TunCompleteRequest(Ctx, irp, STATUS_SUCCESS, IO_NETWORK_INCREMENT);
irp = NULL; irp = NULL;
} }
if (nbl) if (nbl)
TunNBLRefDec(ctx, nbl, 0); TunNBLRefDec(Ctx, nbl, 0);
} }
} }
_IRQL_requires_same_ static void _IRQL_requires_same_ static void
TunSetNBLStatus(_Inout_opt_ NET_BUFFER_LIST *nbl, _In_ NDIS_STATUS status) TunSetNBLStatus(_Inout_opt_ NET_BUFFER_LIST *Nbl, _In_ NDIS_STATUS Status)
{ {
for (; nbl; nbl = NET_BUFFER_LIST_NEXT_NBL(nbl)) for (; Nbl; Nbl = NET_BUFFER_LIST_NEXT_NBL(Nbl))
NET_BUFFER_LIST_STATUS(nbl) = status; NET_BUFFER_LIST_STATUS(Nbl) = Status;
} }
static MINIPORT_SEND_NET_BUFFER_LISTS TunSendNetBufferLists; static MINIPORT_SEND_NET_BUFFER_LISTS TunSendNetBufferLists;
@ -746,26 +748,26 @@ TunCancelSend(NDIS_HANDLE MiniportAdapterContext, PVOID CancelId)
_IRQL_requires_max_(APC_LEVEL) _IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_ _Must_inspect_result_
static NTSTATUS static NTSTATUS
TunDispatchRead(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp) TunDispatchRead(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp)
{ {
NTSTATUS status = TunMapIrp(Irp); NTSTATUS status = TunMapIrp(Irp);
if (!NT_SUCCESS(status)) if (!NT_SUCCESS(status))
goto cleanup_CompleteRequest; goto cleanup_CompleteRequest;
KIRQL irql = ExAcquireSpinLockShared(&ctx->TransitionLock); KIRQL irql = ExAcquireSpinLockShared(&Ctx->TransitionLock);
LONG flags = InterlockedGet(&ctx->Flags); LONG flags = InterlockedGet(&Ctx->Flags);
if ((status = STATUS_FILE_FORCED_CLOSED, !(flags & TUN_FLAGS_PRESENT)) || if ((status = STATUS_FILE_FORCED_CLOSED, !(flags & TUN_FLAGS_PRESENT)) ||
!NT_SUCCESS(status = IoCsqInsertIrpEx(&ctx->Device.ReadQueue.Csq, Irp, NULL, TUN_CSQ_INSERT_TAIL))) !NT_SUCCESS(status = IoCsqInsertIrpEx(&Ctx->Device.ReadQueue.Csq, Irp, NULL, TUN_CSQ_INSERT_TAIL)))
goto cleanup_ExReleaseSpinLockShared; goto cleanup_ExReleaseSpinLockShared;
TunQueueProcess(ctx); TunQueueProcess(Ctx);
ExReleaseSpinLockShared(&ctx->TransitionLock, irql); ExReleaseSpinLockShared(&Ctx->TransitionLock, irql);
return STATUS_PENDING; return STATUS_PENDING;
cleanup_ExReleaseSpinLockShared: cleanup_ExReleaseSpinLockShared:
ExReleaseSpinLockShared(&ctx->TransitionLock, irql); ExReleaseSpinLockShared(&Ctx->TransitionLock, irql);
cleanup_CompleteRequest: cleanup_CompleteRequest:
TunCompleteRequest(ctx, Irp, status, IO_NO_INCREMENT); TunCompleteRequest(Ctx, Irp, status, IO_NO_INCREMENT);
return status; return status;
} }
@ -775,17 +777,17 @@ cleanup_CompleteRequest:
_IRQL_requires_max_(APC_LEVEL) _IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_ _Must_inspect_result_
static NTSTATUS static NTSTATUS
TunDispatchWrite(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp) TunDispatchWrite(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp)
{ {
NTSTATUS status; NTSTATUS status;
InterlockedIncrement64(&ctx->ActiveNBLCount); InterlockedIncrement64(&Ctx->ActiveNBLCount);
if (!NT_SUCCESS(status = TunMapIrp(Irp))) if (!NT_SUCCESS(status = TunMapIrp(Irp)))
goto cleanup_CompleteRequest; goto cleanup_CompleteRequest;
KIRQL irql = ExAcquireSpinLockShared(&ctx->TransitionLock); KIRQL irql = ExAcquireSpinLockShared(&Ctx->TransitionLock);
LONG flags = InterlockedGet(&ctx->Flags); LONG flags = InterlockedGet(&Ctx->Flags);
if (status = STATUS_FILE_FORCED_CLOSED, !(flags & TUN_FLAGS_PRESENT)) if (status = STATUS_FILE_FORCED_CLOSED, !(flags & TUN_FLAGS_PRESENT))
goto cleanup_ExReleaseSpinLockShared; goto cleanup_ExReleaseSpinLockShared;
@ -850,14 +852,14 @@ TunDispatchWrite(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
} }
NET_BUFFER_LIST *nbl = NET_BUFFER_LIST *nbl =
NdisAllocateNetBufferAndNetBufferList(ctx->NBLPool, 0, 0, mdl, (ULONG)(p->Data - buffer), p->Size); NdisAllocateNetBufferAndNetBufferList(Ctx->NBLPool, 0, 0, mdl, (ULONG)(p->Data - buffer), p->Size);
if (!nbl) if (!nbl)
{ {
status = STATUS_INSUFFICIENT_RESOURCES; status = STATUS_INSUFFICIENT_RESOURCES;
goto cleanup_nbl_queues; goto cleanup_nbl_queues;
} }
nbl->SourceHandle = ctx->MiniportAdapterHandle; nbl->SourceHandle = Ctx->MiniportAdapterHandle;
NdisSetNblFlag(nbl, ether_const[idx].nbl_flags); NdisSetNblFlag(nbl, ether_const[idx].nbl_flags);
NET_BUFFER_LIST_INFO(nbl, NetBufferListFrameType) = (PVOID)ether_const[idx].nbl_proto; NET_BUFFER_LIST_INFO(nbl, NetBufferListFrameType) = (PVOID)ether_const[idx].nbl_proto;
NET_BUFFER_LIST_STATUS(nbl) = NDIS_STATUS_SUCCESS; NET_BUFFER_LIST_STATUS(nbl) = NDIS_STATUS_SUCCESS;
@ -882,33 +884,33 @@ TunDispatchWrite(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
} }
if (!(flags & TUN_FLAGS_RUNNING)) if (!(flags & TUN_FLAGS_RUNNING))
{ {
InterlockedAdd64((LONG64 *)&ctx->Statistics.ifInDiscards, nbl_count); InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifInDiscards, nbl_count);
InterlockedAdd64((LONG64 *)&ctx->Statistics.ifInErrors, nbl_count); InterlockedAdd64((LONG64 *)&Ctx->Statistics.ifInErrors, nbl_count);
status = STATUS_SUCCESS; status = STATUS_SUCCESS;
goto cleanup_nbl_queues; goto cleanup_nbl_queues;
} }
InterlockedAdd64(&ctx->ActiveNBLCount, nbl_count); InterlockedAdd64(&Ctx->ActiveNBLCount, nbl_count);
InterlockedExchange(IRP_REFCOUNT(Irp), nbl_count); InterlockedExchange(IRP_REFCOUNT(Irp), nbl_count);
IoMarkIrpPending(Irp); IoMarkIrpPending(Irp);
if (nbl_queue[ethtypeidx_ipv4].head) if (nbl_queue[ethtypeidx_ipv4].head)
NdisMIndicateReceiveNetBufferLists( NdisMIndicateReceiveNetBufferLists(
ctx->MiniportAdapterHandle, Ctx->MiniportAdapterHandle,
nbl_queue[ethtypeidx_ipv4].head, nbl_queue[ethtypeidx_ipv4].head,
NDIS_DEFAULT_PORT_NUMBER, NDIS_DEFAULT_PORT_NUMBER,
nbl_queue[ethtypeidx_ipv4].count, nbl_queue[ethtypeidx_ipv4].count,
NDIS_RECEIVE_FLAGS_SINGLE_ETHER_TYPE); NDIS_RECEIVE_FLAGS_SINGLE_ETHER_TYPE);
if (nbl_queue[ethtypeidx_ipv6].head) if (nbl_queue[ethtypeidx_ipv6].head)
NdisMIndicateReceiveNetBufferLists( NdisMIndicateReceiveNetBufferLists(
ctx->MiniportAdapterHandle, Ctx->MiniportAdapterHandle,
nbl_queue[ethtypeidx_ipv6].head, nbl_queue[ethtypeidx_ipv6].head,
NDIS_DEFAULT_PORT_NUMBER, NDIS_DEFAULT_PORT_NUMBER,
nbl_queue[ethtypeidx_ipv6].count, nbl_queue[ethtypeidx_ipv6].count,
NDIS_RECEIVE_FLAGS_SINGLE_ETHER_TYPE); NDIS_RECEIVE_FLAGS_SINGLE_ETHER_TYPE);
ExReleaseSpinLockShared(&ctx->TransitionLock, irql); ExReleaseSpinLockShared(&Ctx->TransitionLock, irql);
TunCompletePause(ctx, TRUE); TunCompletePause(Ctx, TRUE);
return STATUS_PENDING; return STATUS_PENDING;
cleanup_nbl_queues: cleanup_nbl_queues:
@ -922,10 +924,10 @@ cleanup_nbl_queues:
} }
} }
cleanup_ExReleaseSpinLockShared: cleanup_ExReleaseSpinLockShared:
ExReleaseSpinLockShared(&ctx->TransitionLock, irql); ExReleaseSpinLockShared(&Ctx->TransitionLock, irql);
cleanup_CompleteRequest: cleanup_CompleteRequest:
TunCompleteRequest(ctx, Irp, status, IO_NO_INCREMENT); TunCompleteRequest(Ctx, Irp, status, IO_NO_INCREMENT);
TunCompletePause(ctx, TRUE); TunCompletePause(Ctx, TRUE);
return status; return status;
} }
@ -969,7 +971,7 @@ TunReturnNetBufferLists(NDIS_HANDLE MiniportAdapterContext, PNET_BUFFER_LIST Net
_IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_max_(DISPATCH_LEVEL)
_Must_inspect_result_ _Must_inspect_result_
static NTSTATUS static NTSTATUS
TunDispatchCreate(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp) TunDispatchCreate(_Inout_ TUN_CTX *Ctx, _Inout_ IRP *Irp)
{ {
NTSTATUS status; NTSTATUS status;
TUN_FILE_CTX *file_ctx = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(*file_ctx), TUN_HTONL(TUN_MEMORY_TAG)); TUN_FILE_CTX *file_ctx = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(*file_ctx), TUN_HTONL(TUN_MEMORY_TAG));
@ -977,24 +979,24 @@ TunDispatchCreate(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
return STATUS_INSUFFICIENT_RESOURCES; return STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(file_ctx, sizeof(*file_ctx)); RtlZeroMemory(file_ctx, sizeof(*file_ctx));
KIRQL irql = ExAcquireSpinLockShared(&ctx->TransitionLock); KIRQL irql = ExAcquireSpinLockShared(&Ctx->TransitionLock);
LONG flags = InterlockedGet(&ctx->Flags); LONG flags = InterlockedGet(&Ctx->Flags);
if ((status = STATUS_DELETE_PENDING, !(flags & TUN_FLAGS_PRESENT))) if ((status = STATUS_DELETE_PENDING, !(flags & TUN_FLAGS_PRESENT)))
goto cleanup_ExReleaseSpinLockShared; goto cleanup_ExReleaseSpinLockShared;
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(Irp); IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(Irp);
if (!NT_SUCCESS(status = IoAcquireRemoveLock(&ctx->Device.RemoveLock, stack->FileObject))) if (!NT_SUCCESS(status = IoAcquireRemoveLock(&Ctx->Device.RemoveLock, stack->FileObject)))
goto cleanup_ExReleaseSpinLockShared; goto cleanup_ExReleaseSpinLockShared;
stack->FileObject->FsContext = file_ctx; stack->FileObject->FsContext = file_ctx;
if (InterlockedIncrement64(&ctx->Device.RefCount) == 1) if (InterlockedIncrement64(&Ctx->Device.RefCount) == 1)
TunIndicateStatus(ctx->MiniportAdapterHandle, MediaConnectStateConnected); TunIndicateStatus(Ctx->MiniportAdapterHandle, MediaConnectStateConnected);
status = STATUS_SUCCESS; status = STATUS_SUCCESS;
cleanup_ExReleaseSpinLockShared: cleanup_ExReleaseSpinLockShared:
ExReleaseSpinLockShared(&ctx->TransitionLock, irql); ExReleaseSpinLockShared(&Ctx->TransitionLock, irql);
TunCompleteRequest(ctx, Irp, status, IO_NO_INCREMENT); TunCompleteRequest(Ctx, Irp, status, IO_NO_INCREMENT);
if (!NT_SUCCESS(status)) if (!NT_SUCCESS(status))
ExFreePoolWithTag(file_ctx, TUN_HTONL(TUN_MEMORY_TAG)); ExFreePoolWithTag(file_ctx, TUN_HTONL(TUN_MEMORY_TAG));
return status; return status;
@ -1089,12 +1091,13 @@ TunDispatchPnP(DEVICE_OBJECT *DeviceObject, IRP *Irp)
switch (stack->MinorFunction) switch (stack->MinorFunction)
{ {
case IRP_MN_QUERY_REMOVE_DEVICE: case IRP_MN_QUERY_REMOVE_DEVICE:
case IRP_MN_SURPRISE_REMOVAL: case IRP_MN_SURPRISE_REMOVAL: {
KIRQL irql = ExAcquireSpinLockExclusive(&ctx->TransitionLock); KIRQL irql = ExAcquireSpinLockExclusive(&ctx->TransitionLock);
InterlockedAnd(&ctx->Flags, ~TUN_FLAGS_PRESENT); InterlockedAnd(&ctx->Flags, ~TUN_FLAGS_PRESENT);
ExReleaseSpinLockExclusive(&ctx->TransitionLock, irql); ExReleaseSpinLockExclusive(&ctx->TransitionLock, irql);
TunQueueClear(ctx, NDIS_STATUS_ADAPTER_REMOVED); TunQueueClear(ctx, NDIS_STATUS_ADAPTER_REMOVED);
break; break;
}
case IRP_MN_CANCEL_REMOVE_DEVICE: case IRP_MN_CANCEL_REMOVE_DEVICE:
InterlockedOr(&ctx->Flags, TUN_FLAGS_PRESENT); InterlockedOr(&ctx->Flags, TUN_FLAGS_PRESENT);
@ -1361,8 +1364,7 @@ TunInitializeEx(
/* A miniport driver can call NdisMIndicateStatusEx after setting its /* A miniport driver can call NdisMIndicateStatusEx after setting its
* registration attributes even if the driver is still in the context * registration attributes even if the driver is still in the context
* of the MiniportInitializeEx function. * of the MiniportInitializeEx function. */
*/
TunIndicateStatus(MiniportAdapterHandle, MediaConnectStateDisconnected); TunIndicateStatus(MiniportAdapterHandle, MediaConnectStateDisconnected);
InterlockedIncrement64(&TunAdapterCount); InterlockedIncrement64(&TunAdapterCount);
InterlockedOr(&ctx->Flags, TUN_FLAGS_PRESENT); InterlockedOr(&ctx->Flags, TUN_FLAGS_PRESENT);
@ -1377,7 +1379,7 @@ cleanup_NdisDeregisterDeviceEx:
_IRQL_requires_max_(PASSIVE_LEVEL) _IRQL_requires_max_(PASSIVE_LEVEL)
static NTSTATUS static NTSTATUS
TunDeviceSetDenyAllDacl(_In_ DEVICE_OBJECT *device_object) TunDeviceSetDenyAllDacl(_In_ DEVICE_OBJECT *DeviceObject)
{ {
NTSTATUS status; NTSTATUS status;
SECURITY_DESCRIPTOR sd; SECURITY_DESCRIPTOR sd;
@ -1390,9 +1392,9 @@ TunDeviceSetDenyAllDacl(_In_ DEVICE_OBJECT *device_object)
return status; return status;
if (!NT_SUCCESS(status = RtlSetDaclSecurityDescriptor(&sd, TRUE, &acl, FALSE))) if (!NT_SUCCESS(status = RtlSetDaclSecurityDescriptor(&sd, TRUE, &acl, FALSE)))
return status; return status;
if (!NT_SUCCESS( status = ObOpenObjectByPointer(
status = ObOpenObjectByPointer( DeviceObject, OBJ_KERNEL_HANDLE, NULL, WRITE_DAC, *IoDeviceObjectType, KernelMode, &handle);
device_object, OBJ_KERNEL_HANDLE, NULL, WRITE_DAC, *IoDeviceObjectType, KernelMode, &handle))) if (!NT_SUCCESS(status))
return status; return status;
status = ZwSetSecurityObject(handle, DACL_SECURITY_INFORMATION, &sd); status = ZwSetSecurityObject(handle, DACL_SECURITY_INFORMATION, &sd);
@ -1403,7 +1405,7 @@ TunDeviceSetDenyAllDacl(_In_ DEVICE_OBJECT *device_object)
_IRQL_requires_max_(PASSIVE_LEVEL) _IRQL_requires_max_(PASSIVE_LEVEL)
static void static void
TunForceHandlesClosed(_Inout_ TUN_CTX *ctx) TunForceHandlesClosed(_Inout_ TUN_CTX *Ctx)
{ {
NTSTATUS status; NTSTATUS status;
PEPROCESS process; PEPROCESS process;
@ -1431,9 +1433,9 @@ TunForceHandlesClosed(_Inout_ TUN_CTX *ctx)
for (ULONG_PTR i = 0; i < table->NumberOfHandles; ++i) for (ULONG_PTR i = 0; i < table->NumberOfHandles; ++i)
{ {
FILE_OBJECT *file = FILE_OBJECT *file =
table->Handles[i].Object; // XXX: We should probably first look at table->Handles[i].ObjectTypeIndex, but table->Handles[i].Object; // XXX: We should perhaps first look at table->Handles[i].ObjectTypeIndex, but
// the value changes lots between NT versions. // the value changes lots between NT versions, and it should be implicit anyway.
if (!file || file->Type != 5 || file->DeviceObject != ctx->Device.Object) if (!file || file->Type != 5 || file->DeviceObject != Ctx->Device.Object)
continue; continue;
status = PsLookupProcessByProcessId(table->Handles[i].UniqueProcessId, &process); status = PsLookupProcessByProcessId(table->Handles[i].UniqueProcessId, &process);
if (!NT_SUCCESS(status)) if (!NT_SUCCESS(status))
@ -1479,8 +1481,8 @@ TunHaltEx(NDIS_HANDLE MiniportAdapterContext, NDIS_HALT_ACTION HaltAction)
{ {
TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext; TUN_CTX *ctx = (TUN_CTX *)MiniportAdapterContext;
ASSERT(!InterlockedGet64( ASSERT(!InterlockedGet64(&ctx->ActiveNBLCount)); // Adapter should not be halted if there are (potential)
&ctx->ActiveNBLCount)); /* Adapter should not be halted if there are (potential) active NBLs present. */ // active NBLs present.
KIRQL irql = ExAcquireSpinLockExclusive(&ctx->TransitionLock); KIRQL irql = ExAcquireSpinLockExclusive(&ctx->TransitionLock);
InterlockedAnd(&ctx->Flags, ~TUN_FLAGS_PRESENT); InterlockedAnd(&ctx->Flags, ~TUN_FLAGS_PRESENT);
@ -1521,7 +1523,7 @@ TunShutdownEx(NDIS_HANDLE MiniportAdapterContext, NDIS_SHUTDOWN_ACTION ShutdownA
_IRQL_requires_max_(APC_LEVEL) _IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_ _Must_inspect_result_
static NDIS_STATUS static NDIS_STATUS
TunOidQueryWrite(_Inout_ NDIS_OID_REQUEST *OidRequest, _In_ ULONG value) TunOidQueryWrite(_Inout_ NDIS_OID_REQUEST *OidRequest, _In_ ULONG Value)
{ {
if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(ULONG)) if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(ULONG))
{ {
@ -1531,14 +1533,14 @@ TunOidQueryWrite(_Inout_ NDIS_OID_REQUEST *OidRequest, _In_ ULONG value)
} }
OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof(ULONG); OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof(ULONG);
*(ULONG *)OidRequest->DATA.QUERY_INFORMATION.InformationBuffer = value; *(ULONG *)OidRequest->DATA.QUERY_INFORMATION.InformationBuffer = Value;
return NDIS_STATUS_SUCCESS; return NDIS_STATUS_SUCCESS;
} }
_IRQL_requires_max_(APC_LEVEL) _IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_ _Must_inspect_result_
static NDIS_STATUS static NDIS_STATUS
TunOidQueryWrite32or64(_Inout_ NDIS_OID_REQUEST *OidRequest, _In_ ULONG64 value) TunOidQueryWrite32or64(_Inout_ NDIS_OID_REQUEST *OidRequest, _In_ ULONG64 Value)
{ {
if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(ULONG)) if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < sizeof(ULONG))
{ {
@ -1551,29 +1553,29 @@ TunOidQueryWrite32or64(_Inout_ NDIS_OID_REQUEST *OidRequest, _In_ ULONG64 value)
{ {
OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof(ULONG64); OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = sizeof(ULONG64);
OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof(ULONG); OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof(ULONG);
*(ULONG *)OidRequest->DATA.QUERY_INFORMATION.InformationBuffer = (ULONG)(value & 0xffffffff); *(ULONG *)OidRequest->DATA.QUERY_INFORMATION.InformationBuffer = (ULONG)(Value & 0xffffffff);
return NDIS_STATUS_SUCCESS; return NDIS_STATUS_SUCCESS;
} }
OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof(ULONG64); OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = OidRequest->DATA.QUERY_INFORMATION.BytesWritten = sizeof(ULONG64);
*(ULONG64 *)OidRequest->DATA.QUERY_INFORMATION.InformationBuffer = value; *(ULONG64 *)OidRequest->DATA.QUERY_INFORMATION.InformationBuffer = Value;
return NDIS_STATUS_SUCCESS; return NDIS_STATUS_SUCCESS;
} }
_IRQL_requires_max_(APC_LEVEL) _IRQL_requires_max_(APC_LEVEL)
_Must_inspect_result_ _Must_inspect_result_
static NDIS_STATUS static NDIS_STATUS
TunOidQueryWriteBuf(_Inout_ NDIS_OID_REQUEST *OidRequest, _In_bytecount_(size) const void *buf, _In_ UINT size) TunOidQueryWriteBuf(_Inout_ NDIS_OID_REQUEST *OidRequest, _In_bytecount_(Size) const void *Buf, _In_ UINT Size)
{ {
if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < size) if (OidRequest->DATA.QUERY_INFORMATION.InformationBufferLength < Size)
{ {
OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = size; OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = Size;
OidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0; OidRequest->DATA.QUERY_INFORMATION.BytesWritten = 0;
return NDIS_STATUS_BUFFER_TOO_SHORT; return NDIS_STATUS_BUFFER_TOO_SHORT;
} }
OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = OidRequest->DATA.QUERY_INFORMATION.BytesWritten = size; OidRequest->DATA.QUERY_INFORMATION.BytesNeeded = OidRequest->DATA.QUERY_INFORMATION.BytesWritten = Size;
NdisMoveMemory(OidRequest->DATA.QUERY_INFORMATION.InformationBuffer, buf, size); NdisMoveMemory(OidRequest->DATA.QUERY_INFORMATION.InformationBuffer, Buf, Size);
return NDIS_STATUS_SUCCESS; return NDIS_STATUS_SUCCESS;
} }
@ -1803,9 +1805,8 @@ DriverEntry(DRIVER_OBJECT *DriverObject, UNICODE_STRING *RegistryPath)
.CancelDirectOidRequestHandler = TunCancelDirectOidRequest, .CancelDirectOidRequestHandler = TunCancelDirectOidRequest,
.SynchronousOidRequestHandler = TunSynchronousOidRequest .SynchronousOidRequestHandler = TunSynchronousOidRequest
}; };
if (!NT_SUCCESS( status = NdisMRegisterMiniportDriver(DriverObject, RegistryPath, NULL, &miniport, &NdisMiniportDriverHandle);
status = if (!NT_SUCCESS(status))
NdisMRegisterMiniportDriver(DriverObject, RegistryPath, NULL, &miniport, &NdisMiniportDriverHandle)))
return status; return status;
NdisDispatchPnP = DriverObject->MajorFunction[IRP_MJ_PNP]; NdisDispatchPnP = DriverObject->MajorFunction[IRP_MJ_PNP];