Revise IRP_MJ_WRITE error reporting
The NDIS reason why TunCheckForPause() reported the adapter unavailable, might not make much sense to a client. In case adapter is paused or in low power state, the STATUS_CANCELLED (ERROR_OPERATION_ABORTED) is returned. Exchange buffer size overflow - total TUN_EXCH_MAX_IP_PACKET_SIZE or individual packet - rejects entire exchange buffer now. Exchange buffers containing non-IPv4 or non-IPv6 packets are now rejected as a whole. Allocation errors while preparing NBLs from the exchange buffer are now considered fatal. Ensure write buffer has at least sizeof(TUN_PACKET) left, or reject entire exchange buffer. Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
parent
adefe271a1
commit
b9d0b301b8
62
wintun.c
62
wintun.c
@ -594,26 +594,30 @@ static NTSTATUS TunWriteFromIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
|
|||||||
|
|
||||||
KIRQL irql = ExAcquireSpinLockShared(&ctx->TransitionLock);
|
KIRQL irql = ExAcquireSpinLockShared(&ctx->TransitionLock);
|
||||||
|
|
||||||
if (!NT_SUCCESS(status = TunCheckForPause(ctx)))
|
if (!NT_SUCCESS(TunCheckForPause(ctx))) {
|
||||||
|
status = STATUS_CANCELLED;
|
||||||
goto cleanup_TunCompletePause;
|
goto cleanup_TunCompletePause;
|
||||||
|
}
|
||||||
|
|
||||||
UCHAR *buffer;
|
UCHAR *buffer;
|
||||||
ULONG size;
|
ULONG size;
|
||||||
status = TunGetIrpBuffer(Irp, &buffer, &size);
|
if (!NT_SUCCESS(status = TunGetIrpBuffer(Irp, &buffer, &size)))
|
||||||
if (!NT_SUCCESS(status))
|
|
||||||
goto cleanup_TunCompletePause;
|
goto cleanup_TunCompletePause;
|
||||||
|
|
||||||
const UCHAR *b = buffer, *b_end = buffer + size;
|
const UCHAR *b = buffer, *b_end = buffer + size;
|
||||||
ULONG nbl_count = 0;
|
ULONG nbl_count = 0;
|
||||||
NET_BUFFER_LIST *nbl_head = NULL, *nbl_tail = NULL;
|
NET_BUFFER_LIST *nbl_head = NULL, *nbl_tail = NULL;
|
||||||
LONG64 stat_p_err = 0;
|
while (b + sizeof(TUN_PACKET) <= b_end) {
|
||||||
while (b < b_end) {
|
|
||||||
TUN_PACKET *p = (TUN_PACKET *)b;
|
TUN_PACKET *p = (TUN_PACKET *)b;
|
||||||
if (p->Size > TUN_EXCH_MAX_IP_PACKET_SIZE)
|
if (p->Size > TUN_EXCH_MAX_IP_PACKET_SIZE) {
|
||||||
break;
|
status = STATUS_INVALID_USER_BUFFER;
|
||||||
|
goto cleanup_nbl_head;
|
||||||
|
}
|
||||||
UINT p_size = TunPacketAlign(sizeof(TUN_PACKET) + p->Size);
|
UINT p_size = TunPacketAlign(sizeof(TUN_PACKET) + p->Size);
|
||||||
if (b + p_size > b_end)
|
if (b + p_size > b_end) {
|
||||||
break;
|
status = STATUS_INVALID_USER_BUFFER;
|
||||||
|
goto cleanup_nbl_head;
|
||||||
|
}
|
||||||
|
|
||||||
ULONG nbl_flags;
|
ULONG nbl_flags;
|
||||||
USHORT nbl_proto;
|
USHORT nbl_proto;
|
||||||
@ -624,16 +628,22 @@ static NTSTATUS TunWriteFromIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
|
|||||||
nbl_flags = NDIS_NBL_FLAGS_IS_IPV6;
|
nbl_flags = NDIS_NBL_FLAGS_IS_IPV6;
|
||||||
nbl_proto = NDIS_ETH_TYPE_IPV6;
|
nbl_proto = NDIS_ETH_TYPE_IPV6;
|
||||||
} else {
|
} else {
|
||||||
goto skip_packet;
|
status = STATUS_INVALID_USER_BUFFER;
|
||||||
|
goto cleanup_nbl_head;
|
||||||
}
|
}
|
||||||
|
|
||||||
MDL *mdl = NdisAllocateMdl(ctx->MiniportAdapterHandle, p->Data, p->Size);
|
MDL *mdl = NdisAllocateMdl(ctx->MiniportAdapterHandle, p->Data, p->Size);
|
||||||
if (!mdl)
|
if (!mdl) {
|
||||||
goto skip_packet;
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
goto cleanup_nbl_head;
|
||||||
|
}
|
||||||
|
|
||||||
NET_BUFFER_LIST *nbl = NdisAllocateNetBufferAndNetBufferList(ctx->NBLPool, 0, 0, mdl, 0, p->Size);
|
NET_BUFFER_LIST *nbl = NdisAllocateNetBufferAndNetBufferList(ctx->NBLPool, 0, 0, mdl, 0, p->Size);
|
||||||
if (!nbl)
|
if (!nbl) {
|
||||||
goto cleanup_NdisFreeMdl;
|
NdisFreeMdl(mdl);
|
||||||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
||||||
|
goto cleanup_nbl_head;
|
||||||
|
}
|
||||||
|
|
||||||
nbl->SourceHandle = ctx->MiniportAdapterHandle;
|
nbl->SourceHandle = ctx->MiniportAdapterHandle;
|
||||||
NdisSetNblFlag(nbl, nbl_flags);
|
NdisSetNblFlag(nbl, nbl_flags);
|
||||||
@ -642,16 +652,14 @@ static NTSTATUS TunWriteFromIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
|
|||||||
NET_BUFFER_LIST_IRP(nbl) = Irp;
|
NET_BUFFER_LIST_IRP(nbl) = Irp;
|
||||||
TunAppendNBL(&nbl_head, &nbl_tail, nbl);
|
TunAppendNBL(&nbl_head, &nbl_tail, nbl);
|
||||||
nbl_count++;
|
nbl_count++;
|
||||||
goto next_packet;
|
|
||||||
|
|
||||||
cleanup_NdisFreeMdl:
|
|
||||||
NdisFreeMdl(mdl);
|
|
||||||
skip_packet:
|
|
||||||
stat_p_err++;
|
|
||||||
next_packet:
|
|
||||||
b += p_size;
|
b += p_size;
|
||||||
}
|
}
|
||||||
InterlockedAdd64((LONG64 *)&ctx->Statistics.ifInErrors, stat_p_err);
|
|
||||||
|
if ((ULONG)(b - buffer) != size) {
|
||||||
|
status = STATUS_INVALID_USER_BUFFER;
|
||||||
|
goto cleanup_nbl_head;
|
||||||
|
}
|
||||||
|
Irp->IoStatus.Information = size;
|
||||||
|
|
||||||
if (!nbl_head) {
|
if (!nbl_head) {
|
||||||
status = STATUS_SUCCESS;
|
status = STATUS_SUCCESS;
|
||||||
@ -665,6 +673,13 @@ static NTSTATUS TunWriteFromIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp)
|
|||||||
ExReleaseSpinLockShared(&ctx->TransitionLock, irql);
|
ExReleaseSpinLockShared(&ctx->TransitionLock, irql);
|
||||||
return STATUS_PENDING;
|
return STATUS_PENDING;
|
||||||
|
|
||||||
|
cleanup_nbl_head:
|
||||||
|
for (NET_BUFFER_LIST *nbl = nbl_head, *nbl_next; nbl; nbl = nbl_next) {
|
||||||
|
nbl_next = NET_BUFFER_LIST_NEXT_NBL(nbl);
|
||||||
|
NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL;
|
||||||
|
NdisFreeMdl(NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(nbl)));
|
||||||
|
NdisFreeNetBufferList(nbl);
|
||||||
|
}
|
||||||
cleanup_TunCompletePause:
|
cleanup_TunCompletePause:
|
||||||
TunCompletePause(ctx, TRUE);
|
TunCompletePause(ctx, TRUE);
|
||||||
ExReleaseSpinLockShared(&ctx->TransitionLock, irql);
|
ExReleaseSpinLockShared(&ctx->TransitionLock, irql);
|
||||||
@ -858,8 +873,7 @@ static void TunReturnNetBufferLists(NDIS_HANDLE MiniportAdapterContext, PNET_BUF
|
|||||||
NdisFreeNetBufferList(nbl);
|
NdisFreeNetBufferList(nbl);
|
||||||
|
|
||||||
if (InterlockedDecrement64(IRP_REFCOUNT(irp)) <= 0) {
|
if (InterlockedDecrement64(IRP_REFCOUNT(irp)) <= 0) {
|
||||||
IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
|
TunCompleteRequest(ctx, irp, irp->IoStatus.Information, STATUS_SUCCESS);
|
||||||
TunCompleteRequest(ctx, irp, stack->Parameters.Write.Length, STATUS_SUCCESS);
|
|
||||||
TunCompletePause(ctx, TRUE);
|
TunCompletePause(ctx, TRUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user