Changeset - r25853:b48d7959e189
[Not reviewed]
master
0 2 0
Patric Stout - 3 years ago 2021-07-21 19:55:30
truebrain@openttd.org
Fix: crash when joining a server again after a TCP disconnect (#9453)

"my_client" wasn't always free'd when a game ended. "my_client"
keeps a reference inside the PT_NCLIENT pool. The rest of the
code assumes that when you are not in a game, it can freely
reset this pool.
In result: several ways to trigger a use-after-free.
2 files changed with 14 insertions and 19 deletions:
0 comments (0 inline, 0 general)
src/network/core/tcp_game.cpp
Show inline comments
 
@@ -48,7 +48,7 @@ NetworkRecvStatus NetworkGameSocketHandl
 
		_networking = false;
 
		ShowErrorMessage(STR_NETWORK_ERROR_LOSTCONNECTION, INVALID_STRING_ID, WL_CRITICAL);
 

	
 
		return NETWORK_RECV_STATUS_CLIENT_QUIT;
 
		return this->CloseConnection(NETWORK_RECV_STATUS_CLIENT_QUIT);
 
	}
 

	
 
	return this->CloseConnection(NETWORK_RECV_STATUS_CONNECTION_LOST);
src/network/network_client.cpp
Show inline comments
 
@@ -160,24 +160,19 @@ ClientNetworkGameSocketHandler::~ClientN
 
NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvStatus status)
 
{
 
	assert(status != NETWORK_RECV_STATUS_OKAY);
 
	/*
 
	 * Sending a message just before leaving the game calls cs->SendPackets.
 
	 * This might invoke this function, which means that when we close the
 
	 * connection after cs->SendPackets we will close an already closed
 
	 * 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;
 
	assert(this->sock != INVALID_SOCKET);
 

	
 
	if (!this->HasClientQuit()) {
 
		Debug(net, 3, "Closed client connection {}", this->client_id);
 

	
 
		this->SendPackets(true);
 

	
 
	Debug(net, 3, "Closed client connection {}", this->client_id);
 

	
 
	this->SendPackets(true);
 

	
 
	/* Wait a number of ticks so our leave message can reach the server.
 
	 * This is especially needed for Windows servers as they seem to get
 
	 * the "socket is closed" message before receiving our leave message,
 
	 * which would trigger the server to close the connection as well. */
 
	CSleep(3 * MILLISECONDS_PER_TICK);
 
		/* Wait a number of ticks so our leave message can reach the server.
 
		* This is especially needed for Windows servers as they seem to get
 
		* the "socket is closed" message before receiving our leave message,
 
		* which would trigger the server to close the connection as well. */
 
		CSleep(3 * MILLISECONDS_PER_TICK);
 
	}
 

	
 
	delete this;
 

	
 
@@ -256,7 +251,7 @@ void ClientNetworkGameSocketHandler::Cli
 
/* static */ void ClientNetworkGameSocketHandler::Send()
 
{
 
	my_client->SendPackets();
 
	my_client->CheckConnection();
 
	if (my_client != nullptr) my_client->CheckConnection();
 
}
 

	
 
/**
0 comments (0 inline, 0 general)