- Fix a bug during socket closure that could cause buffered data to be lost
[LWIP]
- Only signal errors when the packet queue is empty to avoid dropping received data
- Use pbuf_copy_partial to copy pbuf chains instead of doing it manually which had a bug
CORE-6655 #resolve CORE-6741 #resolve

svn path=/trunk/; revision=57975
This commit is contained in:
Cameron Gutman 2012-12-23 09:53:36 +00:00
parent 66d645bf61
commit a686746f88
6 changed files with 124 additions and 100 deletions

View file

@ -610,6 +610,7 @@ DisconnectComplete(PDEVICE_OBJECT DeviceObject,
/* Signal complete connection closure immediately */ /* Signal complete connection closure immediately */
FCB->PollState |= AFD_EVENT_ABORT; FCB->PollState |= AFD_EVENT_ABORT;
FCB->PollStatus[FD_CLOSE_BIT] = Irp->IoStatus.Status; FCB->PollStatus[FD_CLOSE_BIT] = Irp->IoStatus.Status;
FCB->LastReceiveStatus = STATUS_FILE_CLOSED;
PollReeval(FCB->DeviceExt, FCB->FileObject); PollReeval(FCB->DeviceExt, FCB->FileObject);
} }
@ -708,8 +709,8 @@ AfdDisconnect(PDEVICE_OBJECT DeviceObject, PIRP Irp,
/* Mark us as overread to complete future reads with an error */ /* Mark us as overread to complete future reads with an error */
FCB->Overread = TRUE; FCB->Overread = TRUE;
/* Set a successful close status to indicate a shutdown on overread */ /* Set a successful receive status to indicate a shutdown on overread */
FCB->PollStatus[FD_CLOSE_BIT] = STATUS_SUCCESS; FCB->LastReceiveStatus = STATUS_SUCCESS;
/* Clear the receive event */ /* Clear the receive event */
FCB->PollState &= ~AFD_EVENT_RECEIVE; FCB->PollState &= ~AFD_EVENT_RECEIVE;

View file

@ -52,11 +52,12 @@ static VOID RefillSocketBuffer( PAFD_FCB FCB )
static VOID HandleReceiveComplete( PAFD_FCB FCB, NTSTATUS Status, ULONG_PTR Information ) static VOID HandleReceiveComplete( PAFD_FCB FCB, NTSTATUS Status, ULONG_PTR Information )
{ {
FCB->LastReceiveStatus = Status;
/* We got closed while the receive was in progress */ /* We got closed while the receive was in progress */
if (FCB->TdiReceiveClosed) if (FCB->TdiReceiveClosed)
{ {
FCB->Recv.Content = 0; /* The received data is discarded */
FCB->Recv.BytesUsed = 0;
} }
/* Receive successful */ /* Receive successful */
else if (Status == STATUS_SUCCESS) else if (Status == STATUS_SUCCESS)
@ -67,30 +68,20 @@ static VOID HandleReceiveComplete( PAFD_FCB FCB, NTSTATUS Status, ULONG_PTR Info
/* Check for graceful closure */ /* Check for graceful closure */
if (Information == 0) if (Information == 0)
{ {
/* Receive is closed */
FCB->TdiReceiveClosed = TRUE; FCB->TdiReceiveClosed = TRUE;
/* Signal graceful receive shutdown */
FCB->PollState |= AFD_EVENT_DISCONNECT;
FCB->PollStatus[FD_CLOSE_BIT] = Status;
PollReeval( FCB->DeviceExt, FCB->FileObject );
} }
else
/* Issue another receive IRP to keep the buffer well stocked */ {
RefillSocketBuffer(FCB); /* Issue another receive IRP to keep the buffer well stocked */
RefillSocketBuffer(FCB);
}
} }
/* Receive failed with no data (unexpected closure) */ /* Receive failed with no data (unexpected closure) */
else else
{ {
FCB->Recv.BytesUsed = 0; /* Previously received data remains intact */
FCB->Recv.Content = 0;
FCB->TdiReceiveClosed = TRUE; FCB->TdiReceiveClosed = TRUE;
/* Signal complete connection failure immediately */
FCB->PollState |= AFD_EVENT_CLOSE;
FCB->PollStatus[FD_CLOSE_BIT] = Status;
PollReeval( FCB->DeviceExt, FCB->FileObject );
} }
} }
@ -168,9 +159,6 @@ static NTSTATUS ReceiveActivity( PAFD_FCB FCB, PIRP Irp ) {
AFD_DbgPrint(MID_TRACE,("%x %x\n", FCB, Irp)); AFD_DbgPrint(MID_TRACE,("%x %x\n", FCB, Irp));
/* Kick the user that receive would be possible now */
/* XXX Not implemented yet */
AFD_DbgPrint(MID_TRACE,("FCB %x Receive data waiting %d\n", AFD_DbgPrint(MID_TRACE,("FCB %x Receive data waiting %d\n",
FCB, FCB->Recv.Content)); FCB, FCB->Recv.Content));
@ -188,7 +176,7 @@ static NTSTATUS ReceiveActivity( PAFD_FCB FCB, PIRP Irp ) {
TotalBytesCopied)); TotalBytesCopied));
UnlockBuffers( RecvReq->BufferArray, UnlockBuffers( RecvReq->BufferArray,
RecvReq->BufferCount, FALSE ); RecvReq->BufferCount, FALSE );
if (FCB->Overread && FCB->PollStatus[FD_CLOSE_BIT] == STATUS_SUCCESS) if (FCB->Overread && FCB->LastReceiveStatus == STATUS_SUCCESS)
{ {
/* Overread after a graceful disconnect so complete with an error */ /* Overread after a graceful disconnect so complete with an error */
Status = STATUS_FILE_CLOSED; Status = STATUS_FILE_CLOSED;
@ -196,7 +184,7 @@ static NTSTATUS ReceiveActivity( PAFD_FCB FCB, PIRP Irp ) {
else else
{ {
/* Unexpected disconnect by the remote host or initial read after a graceful disconnnect */ /* Unexpected disconnect by the remote host or initial read after a graceful disconnnect */
Status = FCB->PollStatus[FD_CLOSE_BIT]; Status = FCB->LastReceiveStatus;
} }
NextIrp->IoStatus.Status = Status; NextIrp->IoStatus.Status = Status;
NextIrp->IoStatus.Information = 0; NextIrp->IoStatus.Information = 0;
@ -260,6 +248,21 @@ static NTSTATUS ReceiveActivity( PAFD_FCB FCB, PIRP Irp ) {
FCB->PollState &= ~AFD_EVENT_RECEIVE; FCB->PollState &= ~AFD_EVENT_RECEIVE;
} }
/* Signal FD_CLOSE if no buffered data remains and the socket can't receive any more */
if (CantReadMore(FCB))
{
if (FCB->LastReceiveStatus == STATUS_SUCCESS)
{
FCB->PollState |= AFD_EVENT_DISCONNECT;
}
else
{
FCB->PollState |= AFD_EVENT_CLOSE;
}
FCB->PollStatus[FD_CLOSE_BIT] = FCB->LastReceiveStatus;
PollReeval(FCB->DeviceExt, FCB->FileObject);
}
AFD_DbgPrint(MID_TRACE,("RetStatus for irp %x is %x\n", Irp, RetStatus)); AFD_DbgPrint(MID_TRACE,("RetStatus for irp %x is %x\n", Irp, RetStatus));
/* Sometimes we're called with a NULL Irp */ /* Sometimes we're called with a NULL Irp */

View file

@ -203,6 +203,7 @@ typedef struct _AFD_FCB {
PVOID Context; PVOID Context;
DWORD PollState; DWORD PollState;
NTSTATUS PollStatus[FD_MAX_EVENTS]; NTSTATUS PollStatus[FD_MAX_EVENTS];
NTSTATUS LastReceiveStatus;
UINT ContextSize; UINT ContextSize;
PVOID ConnectData; PVOID ConnectData;
UINT FilledConnectData; UINT FilledConnectData;

View file

@ -278,6 +278,7 @@ typedef struct _CONNECTION_ENDPOINT {
/* Socket state */ /* Socket state */
BOOLEAN SendShutdown; BOOLEAN SendShutdown;
BOOLEAN ReceiveShutdown; BOOLEAN ReceiveShutdown;
NTSTATUS ReceiveShutdownStatus;
struct _CONNECTION_ENDPOINT *Next; /* Next connection in address file list */ struct _CONNECTION_ENDPOINT *Next; /* Next connection in address file list */
} CONNECTION_ENDPOINT, *PCONNECTION_ENDPOINT; } CONNECTION_ENDPOINT, *PCONNECTION_ENDPOINT;

View file

@ -260,61 +260,53 @@ FlushAllQueues(PCONNECTION_ENDPOINT Connection, NTSTATUS Status)
VOID VOID
TCPFinEventHandler(void *arg, const err_t err) TCPFinEventHandler(void *arg, const err_t err)
{ {
PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg, LastConnection; PCONNECTION_ENDPOINT Connection = (PCONNECTION_ENDPOINT)arg, LastConnection;
const NTSTATUS Status = TCPTranslateError(err); const NTSTATUS Status = TCPTranslateError(err);
KIRQL OldIrql; KIRQL OldIrql;
ASSERT(Connection->AddressFile); ASSERT(Connection->AddressFile);
ASSERT(err != ERR_OK);
/* Check if this was a partial socket closure */ /* First off all, remove the PCB pointer */
if (err == ERR_OK && Connection->SocketContext) Connection->SocketContext = NULL;
{
/* Just flush the receive queue and get out of here */
FlushReceiveQueue(Connection, STATUS_SUCCESS, TRUE);
}
else
{
/* First off all, remove the PCB pointer */
Connection->SocketContext = NULL;
/* Complete all outstanding requests now */ /* Complete all outstanding requests now */
FlushAllQueues(Connection, Status); FlushAllQueues(Connection, Status);
LockObject(Connection, &OldIrql); LockObject(Connection, &OldIrql);
LockObjectAtDpcLevel(Connection->AddressFile); LockObjectAtDpcLevel(Connection->AddressFile);
/* Unlink this connection from the address file */ /* Unlink this connection from the address file */
if (Connection->AddressFile->Connection == Connection) if (Connection->AddressFile->Connection == Connection)
{ {
Connection->AddressFile->Connection = Connection->Next; Connection->AddressFile->Connection = Connection->Next;
DereferenceObject(Connection); DereferenceObject(Connection);
} }
else if (Connection->AddressFile->Listener == Connection) else if (Connection->AddressFile->Listener == Connection)
{ {
Connection->AddressFile->Listener = NULL; Connection->AddressFile->Listener = NULL;
DereferenceObject(Connection); DereferenceObject(Connection);
} }
else else
{ {
LastConnection = Connection->AddressFile->Connection; LastConnection = Connection->AddressFile->Connection;
while (LastConnection->Next != Connection && LastConnection->Next != NULL) while (LastConnection->Next != Connection && LastConnection->Next != NULL)
LastConnection = LastConnection->Next; LastConnection = LastConnection->Next;
if (LastConnection->Next == Connection) if (LastConnection->Next == Connection)
{ {
LastConnection->Next = Connection->Next; LastConnection->Next = Connection->Next;
DereferenceObject(Connection); DereferenceObject(Connection);
} }
} }
UnlockObjectFromDpcLevel(Connection->AddressFile); UnlockObjectFromDpcLevel(Connection->AddressFile);
/* Remove the address file from this connection */ /* Remove the address file from this connection */
DereferenceObject(Connection->AddressFile); DereferenceObject(Connection->AddressFile);
Connection->AddressFile = NULL; Connection->AddressFile = NULL;
UnlockObject(Connection, OldIrql); UnlockObject(Connection, OldIrql);
}
} }
VOID VOID

View file

@ -31,6 +31,9 @@ extern KEVENT TerminationEvent;
extern NPAGED_LOOKASIDE_LIST MessageLookasideList; extern NPAGED_LOOKASIDE_LIST MessageLookasideList;
extern NPAGED_LOOKASIDE_LIST QueueEntryLookasideList; extern NPAGED_LOOKASIDE_LIST QueueEntryLookasideList;
/* Required for ERR_T to NTSTATUS translation in receive error handling */
NTSTATUS TCPTranslateError(const err_t err);
static static
void void
LibTCPEmptyQueue(PCONNECTION_ENDPOINT Connection) LibTCPEmptyQueue(PCONNECTION_ENDPOINT Connection)
@ -84,9 +87,8 @@ NTSTATUS LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection, PUCHA
PQUEUE_ENTRY qp; PQUEUE_ENTRY qp;
struct pbuf* p; struct pbuf* p;
NTSTATUS Status; NTSTATUS Status;
UINT ReadLength, PayloadLength; UINT ReadLength, PayloadLength, Offset, Copied;
KIRQL OldIrql; KIRQL OldIrql;
PUCHAR Payload;
(*Received) = 0; (*Received) = 0;
@ -98,14 +100,14 @@ NTSTATUS LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection, PUCHA
{ {
p = qp->p; p = qp->p;
/* Calculate the payload first */ /* Calculate the payload length first */
Payload = p->payload; PayloadLength = p->tot_len;
Payload += qp->Offset;
PayloadLength = p->len;
PayloadLength -= qp->Offset; PayloadLength -= qp->Offset;
Offset = qp->Offset;
/* Check if we're reading the whole buffer */ /* Check if we're reading the whole buffer */
ReadLength = MIN(PayloadLength, RecvLen); ReadLength = MIN(PayloadLength, RecvLen);
ASSERT(ReadLength != 0);
if (ReadLength != PayloadLength) if (ReadLength != PayloadLength)
{ {
/* Save this one for later */ /* Save this one for later */
@ -116,10 +118,8 @@ NTSTATUS LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection, PUCHA
UnlockObject(Connection, OldIrql); UnlockObject(Connection, OldIrql);
/* Return to a lower IRQL because the receive buffer may be pageable memory */ Copied = pbuf_copy_partial(p, RecvBuffer, ReadLength, Offset);
RtlCopyMemory(RecvBuffer, ASSERT(Copied == ReadLength);
Payload,
ReadLength);
LockObject(Connection, &OldIrql); LockObject(Connection, &OldIrql);
@ -141,6 +141,7 @@ NTSTATUS LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection, PUCHA
ASSERT(RecvLen == 0); ASSERT(RecvLen == 0);
} }
ASSERT((*Received) != 0);
Status = STATUS_SUCCESS; Status = STATUS_SUCCESS;
if (!RecvLen) if (!RecvLen)
@ -150,7 +151,7 @@ NTSTATUS LibTCPGetDataFromConnectionQueue(PCONNECTION_ENDPOINT Connection, PUCHA
else else
{ {
if (Connection->ReceiveShutdown) if (Connection->ReceiveShutdown)
Status = STATUS_SUCCESS; Status = Connection->ReceiveShutdownStatus;
else else
Status = STATUS_PENDING; Status = STATUS_PENDING;
} }
@ -202,8 +203,6 @@ err_t
InternalRecvEventHandler(void *arg, PTCP_PCB pcb, struct pbuf *p, const err_t err) InternalRecvEventHandler(void *arg, PTCP_PCB pcb, struct pbuf *p, const err_t err)
{ {
PCONNECTION_ENDPOINT Connection = arg; PCONNECTION_ENDPOINT Connection = arg;
struct pbuf *pb;
ULONG RecvLen;
/* Make sure the socket didn't get closed */ /* Make sure the socket didn't get closed */
if (!arg) if (!arg)
@ -216,19 +215,9 @@ InternalRecvEventHandler(void *arg, PTCP_PCB pcb, struct pbuf *p, const err_t er
if (p) if (p)
{ {
pb = p; LibTCPEnqueuePacket(Connection, p);
RecvLen = 0;
while (pb != NULL)
{
/* Enqueue this buffer */
LibTCPEnqueuePacket(Connection, pb);
RecvLen += pb->len;
/* Advance and unchain the buffer */ tcp_recved(pcb, p->tot_len);
pb = pbuf_dechain(pb);;
}
tcp_recved(pcb, RecvLen);
TCPRecvEventHandler(arg); TCPRecvEventHandler(arg);
} }
@ -239,7 +228,20 @@ InternalRecvEventHandler(void *arg, PTCP_PCB pcb, struct pbuf *p, const err_t er
* whole socket here (by calling tcp_close()) as that would violate TCP specs * whole socket here (by calling tcp_close()) as that would violate TCP specs
*/ */
Connection->ReceiveShutdown = TRUE; Connection->ReceiveShutdown = TRUE;
TCPFinEventHandler(arg, ERR_OK); Connection->ReceiveShutdownStatus = STATUS_SUCCESS;
/* This code path executes for both remotely and locally initiated closures,
* and we need to distinguish between them */
if (Connection->SocketContext)
{
/* Remotely initiated close */
TCPRecvEventHandler(arg);
}
else
{
/* Locally initated close */
TCPFinEventHandler(arg, ERR_CLSD);
}
} }
return ERR_OK; return ERR_OK;
@ -279,10 +281,31 @@ static
void void
InternalErrorEventHandler(void *arg, const err_t err) InternalErrorEventHandler(void *arg, const err_t err)
{ {
PCONNECTION_ENDPOINT Connection = arg;
KIRQL OldIrql;
/* Make sure the socket didn't get closed */ /* Make sure the socket didn't get closed */
if (!arg) return; if (!arg) return;
TCPFinEventHandler(arg, err); /* Check if data is left to be read */
LockObject(Connection, &OldIrql);
if (IsListEmpty(&Connection->PacketQueue))
{
UnlockObject(Connection, OldIrql);
/* Deliver the error now */
TCPFinEventHandler(arg, err);
}
else
{
UnlockObject(Connection, OldIrql);
/* Defer the error delivery until all data is gone */
Connection->ReceiveShutdown = TRUE;
Connection->ReceiveShutdownStatus = TCPTranslateError(err);
TCPRecvEventHandler(arg);
}
} }
static static
@ -621,7 +644,10 @@ LibTCPShutdownCallback(void *arg)
else else
{ {
if (msg->Input.Shutdown.shut_rx) if (msg->Input.Shutdown.shut_rx)
{
msg->Input.Shutdown.Connection->ReceiveShutdown = TRUE; msg->Input.Shutdown.Connection->ReceiveShutdown = TRUE;
msg->Input.Shutdown.Connection->ReceiveShutdownStatus = STATUS_FILE_CLOSED;
}
if (msg->Input.Shutdown.shut_tx) if (msg->Input.Shutdown.shut_tx)
msg->Input.Shutdown.Connection->SendShutdown = TRUE; msg->Input.Shutdown.Connection->SendShutdown = TRUE;
@ -688,7 +714,7 @@ LibTCPCloseCallback(void *arg)
msg->Output.Close.Error = tcp_close(pcb); msg->Output.Close.Error = tcp_close(pcb);
if (!msg->Output.Close.Error && msg->Input.Close.Callback) if (!msg->Output.Close.Error && msg->Input.Close.Callback)
TCPFinEventHandler(msg->Input.Close.Connection, ERR_OK); TCPFinEventHandler(msg->Input.Close.Connection, ERR_CLSD);
break; break;
default: default: