Skip to content

Commit

Permalink
Fix #11016: Defer deletion of client and server game socket handlers
Browse files Browse the repository at this point in the history
This fixes various use after free scenarios in error handling paths
  • Loading branch information
JGRennison authored and PeterN committed Jun 25, 2023
1 parent 19ae88f commit 4f6d75f
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 7 deletions.
14 changes: 14 additions & 0 deletions src/network/core/tcp_game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

#include "../../safeguards.h"

static std::vector<std::unique_ptr<NetworkGameSocketHandler>> _deferred_deletions;

/**
* Create a new socket for the game connection.
* @param s The socket to connect with.
Expand Down Expand Up @@ -198,3 +200,15 @@ NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MOVE(Packet *p) { ret
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_MOVE(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_MOVE); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_COMPANY_UPDATE(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_COMPANY_UPDATE); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CONFIG_UPDATE(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_CONFIG_UPDATE); }

void NetworkGameSocketHandler::DeferDeletion()
{
_deferred_deletions.emplace_back(this);
this->is_pending_deletion = true;
}

/* static */ void NetworkGameSocketHandler::ProcessDeferredDeletions()
{
/* Calls deleter on all items */
_deferred_deletions.clear();
}
8 changes: 7 additions & 1 deletion src/network/core/tcp_game.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ class CommandQueue {
class NetworkGameSocketHandler : public NetworkTCPSocketHandler {
/* TODO: rewrite into a proper class */
private:
NetworkClientInfo *info; ///< Client info related to this socket
NetworkClientInfo *info; ///< Client info related to this socket
bool is_pending_deletion = false; ///< Whether this socket is pending deletion

protected:
NetworkRecvStatus ReceiveInvalidPacket(PacketGameType type);
Expand Down Expand Up @@ -543,6 +544,11 @@ class NetworkGameSocketHandler : public NetworkTCPSocketHandler {

const char *ReceiveCommand(Packet *p, CommandPacket *cp);
void SendCommand(Packet *p, const CommandPacket *cp);

bool IsPendingDeletion() const { return this->is_pending_deletion; }

void DeferDeletion();
static void ProcessDeferredDeletions();
};

#endif /* NETWORK_CORE_TCP_GAME_H */
10 changes: 8 additions & 2 deletions src/network/network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,7 @@ void NetworkClose(bool close_admins)

_network_coordinator_client.CloseAllConnections();
}
NetworkGameSocketHandler::ProcessDeferredDeletions();

TCPConnecter::KillAll();

Expand Down Expand Up @@ -992,12 +993,15 @@ void NetworkUpdateServerGameType()
*/
static bool NetworkReceive()
{
bool result;
if (_network_server) {
ServerNetworkAdminSocketHandler::Receive();
return ServerNetworkGameSocketHandler::Receive();
result = ServerNetworkGameSocketHandler::Receive();
} else {
return ClientNetworkGameSocketHandler::Receive();
result = ClientNetworkGameSocketHandler::Receive();
}
NetworkGameSocketHandler::ProcessDeferredDeletions();
return result;
}

/* This sends all buffered commands (if possible) */
Expand All @@ -1009,6 +1013,7 @@ static void NetworkSend()
} else {
ClientNetworkGameSocketHandler::Send();
}
NetworkGameSocketHandler::ProcessDeferredDeletions();
}

/**
Expand All @@ -1023,6 +1028,7 @@ void NetworkBackgroundLoop()
TCPConnecter::CheckCallbacks();
NetworkHTTPSocketHandler::HTTPReceive();
QueryNetworkGameSocketHandler::SendReceive();
NetworkGameSocketHandler::ProcessDeferredDeletions();

NetworkBackgroundUDPLoop();
}
Expand Down
6 changes: 5 additions & 1 deletion src/network/network_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ ClientNetworkGameSocketHandler::~ClientNetworkGameSocketHandler()
NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvStatus status)
{
assert(status != NETWORK_RECV_STATUS_OKAY);
if (this->IsPendingDeletion()) return status;

assert(this->sock != INVALID_SOCKET);

if (!this->HasClientQuit()) {
Expand All @@ -174,7 +176,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvSta
CSleep(3 * MILLISECONDS_PER_TICK);
}

delete this;
this->DeferDeletion();

return status;
}
Expand All @@ -185,6 +187,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvSta
*/
void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res)
{
if (this->IsPendingDeletion()) return;

/* First, send a CLIENT_ERROR to the server, so it knows we are
* disconnected (and why!) */
NetworkErrorCode errorno;
Expand Down
7 changes: 4 additions & 3 deletions src/network/network_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ ServerNetworkGameSocketHandler::ServerNetworkGameSocketHandler(SOCKET s) : Netwo
*/
ServerNetworkGameSocketHandler::~ServerNetworkGameSocketHandler()
{
delete this->GetInfo();

if (_redirect_console_to_client == this->client_id) _redirect_console_to_client = INVALID_CLIENT_ID;
OrderBackup::ResetUser(this->client_id);

Expand Down Expand Up @@ -256,7 +258,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta
* connection. This handles that case gracefully without having to make
* that code any more complex or more aware of the validity of the socket.
*/
if (this->sock == INVALID_SOCKET) return status;
if (this->IsPendingDeletion() || this->sock == INVALID_SOCKET) return status;

if (status != NETWORK_RECV_STATUS_CLIENT_QUIT && status != NETWORK_RECV_STATUS_SERVER_ERROR && !this->HasClientQuit() && this->status >= STATUS_AUTHORIZED) {
/* We did not receive a leave message from this client... */
Expand Down Expand Up @@ -292,8 +294,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta

this->SendPackets(true);

delete this->GetInfo();
delete this;
this->DeferDeletion();

InvalidateWindowData(WC_CLIENT_LIST, 0);

Expand Down

0 comments on commit 4f6d75f

Please sign in to comment.