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:
Simon Rozman 2019-05-31 09:57:02 +02:00 committed by Jason A. Donenfeld
parent adefe271a1
commit b9d0b301b8

View File

@ -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);
} }
} }