Server, Client: Fix thread-safety violations related to NBTConnectionReceiveBuffer

This commit is contained in:
Tal Aloni 2024-06-02 10:11:09 +03:00
parent 1a8c94ba94
commit ac403baf41
5 changed files with 180 additions and 165 deletions

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2014-2023 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
/* Copyright (C) 2014-2024 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
@ -172,7 +172,10 @@ namespace SMBLibrary.Client
{
m_clientSocket.Disconnect(false);
m_clientSocket.Close();
m_connectionState.ReceiveBuffer.Dispose();
lock (m_connectionState.ReceiveBuffer)
{
m_connectionState.ReceiveBuffer.Dispose();
}
m_isConnected = false;
m_userID = 0;
}
@ -424,63 +427,66 @@ namespace SMBLibrary.Client
ConnectionState state = (ConnectionState)ar.AsyncState;
Socket clientSocket = state.ClientSocket;
if (!clientSocket.Connected)
lock (state.ReceiveBuffer)
{
state.ReceiveBuffer.Dispose();
return;
}
int numberOfBytesReceived = 0;
try
{
numberOfBytesReceived = clientSocket.EndReceive(ar);
}
catch (ArgumentException) // The IAsyncResult object was not returned from the corresponding synchronous method on this class.
{
state.ReceiveBuffer.Dispose();
return;
}
catch (ObjectDisposedException)
{
Log("[ReceiveCallback] EndReceive ObjectDisposedException");
state.ReceiveBuffer.Dispose();
return;
}
catch (SocketException ex)
{
Log("[ReceiveCallback] EndReceive SocketException: " + ex.Message);
state.ReceiveBuffer.Dispose();
return;
}
if (numberOfBytesReceived == 0)
{
m_isConnected = false;
state.ReceiveBuffer.Dispose();
}
else
{
NBTConnectionReceiveBuffer buffer = state.ReceiveBuffer;
buffer.SetNumberOfBytesReceived(numberOfBytesReceived);
ProcessConnectionBuffer(state);
if (clientSocket.Connected)
if (!clientSocket.Connected)
{
try
state.ReceiveBuffer.Dispose();
return;
}
int numberOfBytesReceived = 0;
try
{
numberOfBytesReceived = clientSocket.EndReceive(ar);
}
catch (ArgumentException) // The IAsyncResult object was not returned from the corresponding synchronous method on this class.
{
state.ReceiveBuffer.Dispose();
return;
}
catch (ObjectDisposedException)
{
Log("[ReceiveCallback] EndReceive ObjectDisposedException");
state.ReceiveBuffer.Dispose();
return;
}
catch (SocketException ex)
{
Log("[ReceiveCallback] EndReceive SocketException: " + ex.Message);
state.ReceiveBuffer.Dispose();
return;
}
if (numberOfBytesReceived == 0)
{
m_isConnected = false;
state.ReceiveBuffer.Dispose();
}
else
{
NBTConnectionReceiveBuffer buffer = state.ReceiveBuffer;
buffer.SetNumberOfBytesReceived(numberOfBytesReceived);
ProcessConnectionBuffer(state);
if (clientSocket.Connected)
{
clientSocket.BeginReceive(buffer.Buffer, buffer.WriteOffset, buffer.AvailableLength, SocketFlags.None, new AsyncCallback(OnClientSocketReceive), state);
}
catch (ObjectDisposedException)
{
m_isConnected = false;
buffer.Dispose();
Log("[ReceiveCallback] BeginReceive ObjectDisposedException");
}
catch (SocketException ex)
{
m_isConnected = false;
buffer.Dispose();
Log("[ReceiveCallback] BeginReceive SocketException: " + ex.Message);
try
{
clientSocket.BeginReceive(buffer.Buffer, buffer.WriteOffset, buffer.AvailableLength, SocketFlags.None, new AsyncCallback(OnClientSocketReceive), state);
}
catch (ObjectDisposedException)
{
m_isConnected = false;
buffer.Dispose();
Log("[ReceiveCallback] BeginReceive ObjectDisposedException");
}
catch (SocketException ex)
{
m_isConnected = false;
buffer.Dispose();
Log("[ReceiveCallback] BeginReceive SocketException: " + ex.Message);
}
}
}
}

View file

@ -182,7 +182,10 @@ namespace SMBLibrary.Client
{
m_clientSocket.Disconnect(false);
m_clientSocket.Close();
m_connectionState.ReceiveBuffer.Dispose();
lock (m_connectionState.ReceiveBuffer)
{
m_connectionState.ReceiveBuffer.Dispose();
}
m_isConnected = false;
m_messageID = 0;
m_sessionID = 0;
@ -357,63 +360,66 @@ namespace SMBLibrary.Client
ConnectionState state = (ConnectionState)ar.AsyncState;
Socket clientSocket = state.ClientSocket;
if (!clientSocket.Connected)
lock (state.ReceiveBuffer)
{
state.ReceiveBuffer.Dispose();
return;
}
int numberOfBytesReceived = 0;
try
{
numberOfBytesReceived = clientSocket.EndReceive(ar);
}
catch (ArgumentException) // The IAsyncResult object was not returned from the corresponding synchronous method on this class.
{
state.ReceiveBuffer.Dispose();
return;
}
catch (ObjectDisposedException)
{
Log("[ReceiveCallback] EndReceive ObjectDisposedException");
state.ReceiveBuffer.Dispose();
return;
}
catch (SocketException ex)
{
Log("[ReceiveCallback] EndReceive SocketException: " + ex.Message);
state.ReceiveBuffer.Dispose();
return;
}
if (numberOfBytesReceived == 0)
{
m_isConnected = false;
state.ReceiveBuffer.Dispose();
}
else
{
NBTConnectionReceiveBuffer buffer = state.ReceiveBuffer;
buffer.SetNumberOfBytesReceived(numberOfBytesReceived);
ProcessConnectionBuffer(state);
if (clientSocket.Connected)
if (!clientSocket.Connected)
{
try
state.ReceiveBuffer.Dispose();
return;
}
int numberOfBytesReceived = 0;
try
{
numberOfBytesReceived = clientSocket.EndReceive(ar);
}
catch (ArgumentException) // The IAsyncResult object was not returned from the corresponding synchronous method on this class.
{
state.ReceiveBuffer.Dispose();
return;
}
catch (ObjectDisposedException)
{
Log("[ReceiveCallback] EndReceive ObjectDisposedException");
state.ReceiveBuffer.Dispose();
return;
}
catch (SocketException ex)
{
Log("[ReceiveCallback] EndReceive SocketException: " + ex.Message);
state.ReceiveBuffer.Dispose();
return;
}
if (numberOfBytesReceived == 0)
{
m_isConnected = false;
state.ReceiveBuffer.Dispose();
}
else
{
NBTConnectionReceiveBuffer buffer = state.ReceiveBuffer;
buffer.SetNumberOfBytesReceived(numberOfBytesReceived);
ProcessConnectionBuffer(state);
if (clientSocket.Connected)
{
clientSocket.BeginReceive(buffer.Buffer, buffer.WriteOffset, buffer.AvailableLength, SocketFlags.None, new AsyncCallback(OnClientSocketReceive), state);
}
catch (ObjectDisposedException)
{
m_isConnected = false;
Log("[ReceiveCallback] BeginReceive ObjectDisposedException");
buffer.Dispose();
}
catch (SocketException ex)
{
m_isConnected = false;
Log("[ReceiveCallback] BeginReceive SocketException: " + ex.Message);
buffer.Dispose();
try
{
clientSocket.BeginReceive(buffer.Buffer, buffer.WriteOffset, buffer.AvailableLength, SocketFlags.None, new AsyncCallback(OnClientSocketReceive), state);
}
catch (ObjectDisposedException)
{
m_isConnected = false;
Log("[ReceiveCallback] BeginReceive ObjectDisposedException");
buffer.Dispose();
}
catch (SocketException ex)
{
m_isConnected = false;
Log("[ReceiveCallback] BeginReceive SocketException: " + ex.Message);
buffer.Dispose();
}
}
}
}

View file

@ -13,11 +13,11 @@ using Utilities;
namespace SMBLibrary.NetBios
{
/// <remarks>
/// NBTConnectionReceiveBuffer is not thread-safe.
/// </remarks>
public class NBTConnectionReceiveBuffer : IDisposable
{
#if NETSTANDARD2_0
private object m_bufferSyncLock = new object();
#endif
private byte[] m_buffer;
private int m_readOffset = 0;
private int m_bytesInBuffer = 0;
@ -131,13 +131,10 @@ namespace SMBLibrary.NetBios
public void Dispose()
{
#if NETSTANDARD2_0
lock (m_bufferSyncLock)
if (m_buffer != null)
{
if (m_buffer != null)
{
ArrayPool<byte>.Shared.Return(m_buffer);
m_buffer = null;
}
ArrayPool<byte>.Shared.Return(m_buffer);
m_buffer = null;
}
#else
m_buffer = null;

View file

@ -42,7 +42,10 @@ namespace SMBLibrary.Server
connection.SendQueue.Stop();
SocketUtils.ReleaseSocket(connection.ClientSocket);
connection.CloseSessions();
connection.ReceiveBuffer.Dispose();
lock (connection.ReceiveBuffer)
{
connection.ReceiveBuffer.Dispose();
}
RemoveConnection(connection);
}

View file

@ -230,63 +230,66 @@ namespace SMBLibrary.Server
ConnectionState state = (ConnectionState)result.AsyncState;
Socket clientSocket = state.ClientSocket;
if (!m_listening)
lock (state.ReceiveBuffer)
{
clientSocket.Close();
return;
}
int numberOfBytesReceived;
try
{
numberOfBytesReceived = clientSocket.EndReceive(result);
}
catch (ObjectDisposedException)
{
state.LogToServer(Severity.Debug, "The connection was terminated");
m_connectionManager.ReleaseConnection(state);
return;
}
catch (SocketException ex)
{
const int WSAECONNRESET = 10054;
if (ex.ErrorCode == WSAECONNRESET)
if (!m_listening)
{
state.LogToServer(Severity.Debug, "The connection was forcibly closed by the remote host");
clientSocket.Close();
return;
}
else
{
state.LogToServer(Severity.Debug, "The connection was terminated, Socket error code: {0}", ex.ErrorCode);
}
m_connectionManager.ReleaseConnection(state);
return;
}
if (numberOfBytesReceived == 0)
{
state.LogToServer(Severity.Debug, "The client closed the connection");
m_connectionManager.ReleaseConnection(state);
return;
}
state.UpdateLastReceiveDT();
NBTConnectionReceiveBuffer receiveBuffer = state.ReceiveBuffer;
receiveBuffer.SetNumberOfBytesReceived(numberOfBytesReceived);
ProcessConnectionBuffer(ref state);
if (clientSocket.Connected)
{
int numberOfBytesReceived;
try
{
clientSocket.BeginReceive(state.ReceiveBuffer.Buffer, state.ReceiveBuffer.WriteOffset, state.ReceiveBuffer.AvailableLength, 0, ReceiveCallback, state);
numberOfBytesReceived = clientSocket.EndReceive(result);
}
catch (ObjectDisposedException)
{
state.LogToServer(Severity.Debug, "The connection was terminated");
m_connectionManager.ReleaseConnection(state);
return;
}
catch (SocketException)
catch (SocketException ex)
{
const int WSAECONNRESET = 10054;
if (ex.ErrorCode == WSAECONNRESET)
{
state.LogToServer(Severity.Debug, "The connection was forcibly closed by the remote host");
}
else
{
state.LogToServer(Severity.Debug, "The connection was terminated, Socket error code: {0}", ex.ErrorCode);
}
m_connectionManager.ReleaseConnection(state);
return;
}
if (numberOfBytesReceived == 0)
{
state.LogToServer(Severity.Debug, "The client closed the connection");
m_connectionManager.ReleaseConnection(state);
return;
}
state.UpdateLastReceiveDT();
NBTConnectionReceiveBuffer receiveBuffer = state.ReceiveBuffer;
receiveBuffer.SetNumberOfBytesReceived(numberOfBytesReceived);
ProcessConnectionBuffer(ref state);
if (clientSocket.Connected)
{
try
{
clientSocket.BeginReceive(state.ReceiveBuffer.Buffer, state.ReceiveBuffer.WriteOffset, state.ReceiveBuffer.AvailableLength, 0, ReceiveCallback, state);
}
catch (ObjectDisposedException)
{
m_connectionManager.ReleaseConnection(state);
}
catch (SocketException)
{
m_connectionManager.ReleaseConnection(state);
}
}
}
}