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); | ||||
| 
 | ||||
| 	if (!NT_SUCCESS(status = TunCheckForPause(ctx))) | ||||
| 	if (!NT_SUCCESS(TunCheckForPause(ctx))) { | ||||
| 		status = STATUS_CANCELLED; | ||||
| 		goto cleanup_TunCompletePause; | ||||
| 	} | ||||
| 
 | ||||
| 	UCHAR *buffer; | ||||
| 	ULONG size; | ||||
| 	status = TunGetIrpBuffer(Irp, &buffer, &size); | ||||
| 	if (!NT_SUCCESS(status)) | ||||
| 	if (!NT_SUCCESS(status = TunGetIrpBuffer(Irp, &buffer, &size))) | ||||
| 		goto cleanup_TunCompletePause; | ||||
| 
 | ||||
| 	const UCHAR *b = buffer, *b_end = buffer + size; | ||||
| 	ULONG nbl_count = 0; | ||||
| 	NET_BUFFER_LIST *nbl_head = NULL, *nbl_tail = NULL; | ||||
| 	LONG64 stat_p_err = 0; | ||||
| 	while (b < b_end) { | ||||
| 	while (b + sizeof(TUN_PACKET) <= b_end) { | ||||
| 		TUN_PACKET *p = (TUN_PACKET *)b; | ||||
| 		if (p->Size > TUN_EXCH_MAX_IP_PACKET_SIZE) | ||||
| 			break; | ||||
| 		if (p->Size > TUN_EXCH_MAX_IP_PACKET_SIZE) { | ||||
| 			status = STATUS_INVALID_USER_BUFFER; | ||||
| 			goto cleanup_nbl_head; | ||||
| 		} | ||||
| 		UINT p_size = TunPacketAlign(sizeof(TUN_PACKET) + p->Size); | ||||
| 		if (b + p_size > b_end) | ||||
| 			break; | ||||
| 		if (b + p_size > b_end) { | ||||
| 			status = STATUS_INVALID_USER_BUFFER; | ||||
| 			goto cleanup_nbl_head; | ||||
| 		} | ||||
| 
 | ||||
| 		ULONG nbl_flags; | ||||
| 		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_proto = NDIS_ETH_TYPE_IPV6; | ||||
| 		} else { | ||||
| 			goto skip_packet; | ||||
| 			status = STATUS_INVALID_USER_BUFFER; | ||||
| 			goto cleanup_nbl_head; | ||||
| 		} | ||||
| 
 | ||||
| 		MDL *mdl = NdisAllocateMdl(ctx->MiniportAdapterHandle, p->Data, p->Size); | ||||
| 		if (!mdl) | ||||
| 			goto skip_packet; | ||||
| 		if (!mdl) { | ||||
| 			status = STATUS_INSUFFICIENT_RESOURCES; | ||||
| 			goto cleanup_nbl_head; | ||||
| 		} | ||||
| 
 | ||||
| 		NET_BUFFER_LIST *nbl = NdisAllocateNetBufferAndNetBufferList(ctx->NBLPool, 0, 0, mdl, 0, p->Size); | ||||
| 		if (!nbl) | ||||
| 			goto cleanup_NdisFreeMdl; | ||||
| 		if (!nbl) { | ||||
| 			NdisFreeMdl(mdl); | ||||
| 			status = STATUS_INSUFFICIENT_RESOURCES; | ||||
| 			goto cleanup_nbl_head; | ||||
| 		} | ||||
| 
 | ||||
| 		nbl->SourceHandle = ctx->MiniportAdapterHandle; | ||||
| 		NdisSetNblFlag(nbl, nbl_flags); | ||||
| @ -642,16 +652,14 @@ static NTSTATUS TunWriteFromIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp) | ||||
| 		NET_BUFFER_LIST_IRP(nbl) = Irp; | ||||
| 		TunAppendNBL(&nbl_head, &nbl_tail, nbl); | ||||
| 		nbl_count++; | ||||
| 		goto next_packet; | ||||
| 
 | ||||
| 	cleanup_NdisFreeMdl: | ||||
| 		NdisFreeMdl(mdl); | ||||
| 	skip_packet: | ||||
| 		stat_p_err++; | ||||
| 	next_packet: | ||||
| 		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) { | ||||
| 		status = STATUS_SUCCESS; | ||||
| @ -665,6 +673,13 @@ static NTSTATUS TunWriteFromIrp(_Inout_ TUN_CTX *ctx, _Inout_ IRP *Irp) | ||||
| 	ExReleaseSpinLockShared(&ctx->TransitionLock, irql); | ||||
| 	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: | ||||
| 	TunCompletePause(ctx, TRUE); | ||||
| 	ExReleaseSpinLockShared(&ctx->TransitionLock, irql); | ||||
| @ -858,8 +873,7 @@ static void TunReturnNetBufferLists(NDIS_HANDLE MiniportAdapterContext, PNET_BUF | ||||
| 		NdisFreeNetBufferList(nbl); | ||||
| 
 | ||||
| 		if (InterlockedDecrement64(IRP_REFCOUNT(irp)) <= 0) { | ||||
| 			IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); | ||||
| 			TunCompleteRequest(ctx, irp, stack->Parameters.Write.Length, STATUS_SUCCESS); | ||||
| 			TunCompleteRequest(ctx, irp, irp->IoStatus.Information, STATUS_SUCCESS); | ||||
| 			TunCompletePause(ctx, TRUE); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user