mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-10 08:00:05 +00:00
Fix #11016: Defer deletion of client and server game socket handlers
This fixes various use after free scenarios in error handling paths
This commit is contained in:
parent
19ae88fb63
commit
4f6d75f97d
@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include "../../safeguards.h"
|
#include "../../safeguards.h"
|
||||||
|
|
||||||
|
static std::vector<std::unique_ptr<NetworkGameSocketHandler>> _deferred_deletions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new socket for the game connection.
|
* Create a new socket for the game connection.
|
||||||
* @param s The socket to connect with.
|
* @param s The socket to connect with.
|
||||||
@ -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_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_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); }
|
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();
|
||||||
|
}
|
||||||
|
@ -154,7 +154,8 @@ public:
|
|||||||
class NetworkGameSocketHandler : public NetworkTCPSocketHandler {
|
class NetworkGameSocketHandler : public NetworkTCPSocketHandler {
|
||||||
/* TODO: rewrite into a proper class */
|
/* TODO: rewrite into a proper class */
|
||||||
private:
|
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:
|
protected:
|
||||||
NetworkRecvStatus ReceiveInvalidPacket(PacketGameType type);
|
NetworkRecvStatus ReceiveInvalidPacket(PacketGameType type);
|
||||||
@ -543,6 +544,11 @@ public:
|
|||||||
|
|
||||||
const char *ReceiveCommand(Packet *p, CommandPacket *cp);
|
const char *ReceiveCommand(Packet *p, CommandPacket *cp);
|
||||||
void SendCommand(Packet *p, const 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 */
|
#endif /* NETWORK_CORE_TCP_GAME_H */
|
||||||
|
@ -595,6 +595,7 @@ void NetworkClose(bool close_admins)
|
|||||||
|
|
||||||
_network_coordinator_client.CloseAllConnections();
|
_network_coordinator_client.CloseAllConnections();
|
||||||
}
|
}
|
||||||
|
NetworkGameSocketHandler::ProcessDeferredDeletions();
|
||||||
|
|
||||||
TCPConnecter::KillAll();
|
TCPConnecter::KillAll();
|
||||||
|
|
||||||
@ -992,12 +993,15 @@ void NetworkUpdateServerGameType()
|
|||||||
*/
|
*/
|
||||||
static bool NetworkReceive()
|
static bool NetworkReceive()
|
||||||
{
|
{
|
||||||
|
bool result;
|
||||||
if (_network_server) {
|
if (_network_server) {
|
||||||
ServerNetworkAdminSocketHandler::Receive();
|
ServerNetworkAdminSocketHandler::Receive();
|
||||||
return ServerNetworkGameSocketHandler::Receive();
|
result = ServerNetworkGameSocketHandler::Receive();
|
||||||
} else {
|
} else {
|
||||||
return ClientNetworkGameSocketHandler::Receive();
|
result = ClientNetworkGameSocketHandler::Receive();
|
||||||
}
|
}
|
||||||
|
NetworkGameSocketHandler::ProcessDeferredDeletions();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This sends all buffered commands (if possible) */
|
/* This sends all buffered commands (if possible) */
|
||||||
@ -1009,6 +1013,7 @@ static void NetworkSend()
|
|||||||
} else {
|
} else {
|
||||||
ClientNetworkGameSocketHandler::Send();
|
ClientNetworkGameSocketHandler::Send();
|
||||||
}
|
}
|
||||||
|
NetworkGameSocketHandler::ProcessDeferredDeletions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1023,6 +1028,7 @@ void NetworkBackgroundLoop()
|
|||||||
TCPConnecter::CheckCallbacks();
|
TCPConnecter::CheckCallbacks();
|
||||||
NetworkHTTPSocketHandler::HTTPReceive();
|
NetworkHTTPSocketHandler::HTTPReceive();
|
||||||
QueryNetworkGameSocketHandler::SendReceive();
|
QueryNetworkGameSocketHandler::SendReceive();
|
||||||
|
NetworkGameSocketHandler::ProcessDeferredDeletions();
|
||||||
|
|
||||||
NetworkBackgroundUDPLoop();
|
NetworkBackgroundUDPLoop();
|
||||||
}
|
}
|
||||||
|
@ -160,6 +160,8 @@ ClientNetworkGameSocketHandler::~ClientNetworkGameSocketHandler()
|
|||||||
NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvStatus status)
|
NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvStatus status)
|
||||||
{
|
{
|
||||||
assert(status != NETWORK_RECV_STATUS_OKAY);
|
assert(status != NETWORK_RECV_STATUS_OKAY);
|
||||||
|
if (this->IsPendingDeletion()) return status;
|
||||||
|
|
||||||
assert(this->sock != INVALID_SOCKET);
|
assert(this->sock != INVALID_SOCKET);
|
||||||
|
|
||||||
if (!this->HasClientQuit()) {
|
if (!this->HasClientQuit()) {
|
||||||
@ -174,7 +176,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvSta
|
|||||||
CSleep(3 * MILLISECONDS_PER_TICK);
|
CSleep(3 * MILLISECONDS_PER_TICK);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete this;
|
this->DeferDeletion();
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@ -185,6 +187,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvSta
|
|||||||
*/
|
*/
|
||||||
void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res)
|
void ClientNetworkGameSocketHandler::ClientError(NetworkRecvStatus res)
|
||||||
{
|
{
|
||||||
|
if (this->IsPendingDeletion()) return;
|
||||||
|
|
||||||
/* First, send a CLIENT_ERROR to the server, so it knows we are
|
/* First, send a CLIENT_ERROR to the server, so it knows we are
|
||||||
* disconnected (and why!) */
|
* disconnected (and why!) */
|
||||||
NetworkErrorCode errorno;
|
NetworkErrorCode errorno;
|
||||||
|
@ -224,6 +224,8 @@ ServerNetworkGameSocketHandler::ServerNetworkGameSocketHandler(SOCKET s) : Netwo
|
|||||||
*/
|
*/
|
||||||
ServerNetworkGameSocketHandler::~ServerNetworkGameSocketHandler()
|
ServerNetworkGameSocketHandler::~ServerNetworkGameSocketHandler()
|
||||||
{
|
{
|
||||||
|
delete this->GetInfo();
|
||||||
|
|
||||||
if (_redirect_console_to_client == this->client_id) _redirect_console_to_client = INVALID_CLIENT_ID;
|
if (_redirect_console_to_client == this->client_id) _redirect_console_to_client = INVALID_CLIENT_ID;
|
||||||
OrderBackup::ResetUser(this->client_id);
|
OrderBackup::ResetUser(this->client_id);
|
||||||
|
|
||||||
@ -256,7 +258,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta
|
|||||||
* connection. This handles that case gracefully without having to make
|
* connection. This handles that case gracefully without having to make
|
||||||
* that code any more complex or more aware of the validity of the socket.
|
* 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) {
|
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... */
|
/* We did not receive a leave message from this client... */
|
||||||
@ -292,8 +294,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta
|
|||||||
|
|
||||||
this->SendPackets(true);
|
this->SendPackets(true);
|
||||||
|
|
||||||
delete this->GetInfo();
|
this->DeferDeletion();
|
||||||
delete this;
|
|
||||||
|
|
||||||
InvalidateWindowData(WC_CLIENT_LIST, 0);
|
InvalidateWindowData(WC_CLIENT_LIST, 0);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user