diff --git a/Source/Engine/Networking/NetworkManager.cpp b/Source/Engine/Networking/NetworkManager.cpp index 783b9fa95..61a2f38f8 100644 --- a/Source/Engine/Networking/NetworkManager.cpp +++ b/Source/Engine/Networking/NetworkManager.cpp @@ -280,6 +280,11 @@ void OnNetworkMessageHandshake(NetworkEvent& event, NetworkClient* client, Netwo connectionData.Result = 0; connectionData.Platform = (PlatformType)msgData.Platform; connectionData.Architecture = (ArchitectureType)msgData.Architecture; + if (event.Message.Position + msgData.PayloadDataSize > event.Message.BufferSize) + { + LOG(Warning, "Invalid payload data size from client id={1}", connectionData.Result, event.Sender.ConnectionId); + return; + } connectionData.PayloadData.Resize(msgData.PayloadDataSize); event.Message.ReadBytes(connectionData.PayloadData.Get(), msgData.PayloadDataSize); if (msgData.EngineProtocolVersion != NETWORK_PROTOCOL_VERSION || @@ -300,7 +305,7 @@ void OnNetworkMessageHandshake(NetworkEvent& event, NetworkClient* client, Netwo // Update client based on connection result if (connectionData.Result != 0) { - LOG(Info, "Connection blocked with result {0} from client id={1}.", connectionData.Result, event.Sender.ConnectionId); + LOG(Info, "Connection blocked with result {0} from client id={1}", connectionData.Result, event.Sender.ConnectionId); client->State = NetworkConnectionState::Disconnecting; peer->Disconnect(event.Sender); client->State = NetworkConnectionState::Disconnected; @@ -753,7 +758,7 @@ void NetworkManagerService::Update() NetworkManager::ClientConnecting(connectionData); // Allow client to validate connection or inject custom connection data if (connectionData.Result != 0) { - LOG(Info, "Connection blocked with result {0}.", connectionData.Result); + LOG(Info, "Connection blocked with result {0}", connectionData.Result); NetworkManager::Stop(); break; } diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 83b09d4a5..002e3c9ea 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -34,6 +34,10 @@ #include "FlaxEngine.Gen.h" #endif +// Max number of parts for replication and RPC messages, in case of large data that is split into smaller chunks +#define NETWORK_MAX_PARTS 64 + +// Max size of the network stream buffer (in bytes) to use for replication and RPC messages #define NETWORK_STREAM_SIZE_LIMIT 65535 #if !BUILD_RELEASE @@ -995,11 +999,15 @@ PartsItem* AddPartsItem(Array& items, NetworkEvent& event, uint32 own item->Data.Resize(dataSize); } + // Validate item and part data + if (item->PartsLeft == 0 || partStart + partSize > item->Data.Count()) + return nullptr; + // Copy part data - ASSERT(item->PartsLeft > 0); item->PartsLeft--; - ASSERT(partStart + partSize <= item->Data.Count()); const void* partData = event.Message.SkipBytes(partSize); + if (!partData) + return nullptr; Platform::MemoryCopy(item->Data.Get() + partStart, partData, partSize); return item; @@ -1087,6 +1095,8 @@ void InvokeObjectSpawn(const NetworkMessageObjectSpawn& msgData, const Guid& pre { auto& msgDataItem = msgDataItems[i]; NetworkReplicatedObject* e = ResolveObject(msgDataItem.ObjectId, msgDataItem.ParentId, msgDataItem.ObjectTypeName); + if (!e) + return; auto& item = *e; item.Spawned = true; if (NetworkManager::IsClient()) @@ -1847,7 +1857,7 @@ bool NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHa return false; if (argsStream && argsStream->HasError()) { - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to write stream with RPC arguments '{}::{}'", type.ToString(), name.ToString()); + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to write stream with RPC arguments {}::{}", type.ToString(), name.ToString()); return true; } PROFILE_MEM(Networking); @@ -2120,10 +2130,7 @@ void NetworkInternal::NetworkReplicatorUpdate() { auto& e = ReplicationParts[i]; if (e.PartsLeft > 0) - { - // TODO: remove replication items after some TTL to reduce memory usage - continue; - } + continue; // Wait for all parts to arrive ScriptingObject* obj = e.Object.Get(); if (obj) { @@ -2143,6 +2150,7 @@ void NetworkInternal::NetworkReplicatorUpdate() // TODO: remove items from RpcParts after some TTL to reduce memory usage // TODO: remove items from SpawnParts after some TTL to reduce memory usage + // TODO: remove items from ReplicationParts after some TTL to reduce memory usage // Replicate all owned networked objects with other clients or server if (!CachedReplicationResult) @@ -2214,6 +2222,11 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo event.Message.ReadNetworkId(parentId); event.Message.ReadNetworkName(objectTypeName); event.Message.ReadStructure(msgDataPayload); + if (msgDataPayload.PartsCount > NETWORK_MAX_PARTS) + { + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Invalid replicate message parts count: {}", msgDataPayload.PartsCount); + return; + } ScopeLock lock(ObjectsLock); if (DespawnedObjects.Contains(objectId)) return; // Skip replicating non-existing objects @@ -2230,13 +2243,21 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo if (msgDataPayload.PartsCount == 1) { // Replicate + if (event.Message.Position + msgDataPayload.DataSize > event.Message.BufferSize) + { + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Ignoring invalid replication data for object {} (data size: {}, bytes left: {})", item.ToString(), msgDataPayload.DataSize, (int32)event.Message.BufferSize - (int32)event.Message.Position); + return; + } InvokeObjectReplication(item, msgData.OwnerFrame, event.Message.Buffer + event.Message.Position, msgDataPayload.DataSize, senderClientId); } else { // Add to replication from multiple parts PartsItem* replicateItem = AddObjectReplicateItem(event, msgData.OwnerFrame, msgDataPayload.PartsCount, msgDataPayload.DataSize, objectId, 0, msgDataPayload.PartSize, senderClientId); - replicateItem->Object = e->Object; + if (replicateItem) + { + replicateItem->Object = e->Object; + } } } @@ -2262,8 +2283,11 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl Guid prefabId = Guid::Empty; event.Message.ReadStructure(msgData); event.Message.ReadNetworkId(prefabId); - if (msgData.ItemsCount == 0) + if (msgData.ItemsCount > NETWORK_MAX_PARTS) + { + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Invalid spawn message parts count: {}", msgData.ItemsCount); return; + } if (msgData.UseParts) { // Allocate spawn message parts collecting @@ -2277,6 +2301,8 @@ void NetworkInternal::OnNetworkMessageObjectSpawn(NetworkEvent& event, NetworkCl else { const auto* msgDataItems = (NetworkMessageObjectSpawnItem*)event.Message.SkipBytes(msgData.ItemsCount * sizeof(NetworkMessageObjectSpawnItem)); + if (!msgDataItems) + return; InvokeObjectSpawn(msgData, prefabId, msgDataItems); } } @@ -2421,6 +2447,11 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie event.Message.ReadNetworkName(rpcTypeName); event.Message.ReadNetworkName(rpcName); event.Message.ReadStructure(msgDataPayload); + if (msgDataPayload.PartsCount > NETWORK_MAX_PARTS) + { + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Invalid RPC parts count: {}", msgDataPayload.PartsCount); + return; + } ScopeLock lock(ObjectsLock); // Find RPC info @@ -2458,14 +2489,22 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie if (msgDataPayload.PartsCount == 1) { // Call RPC + if (event.Message.Position + msgDataPayload.DataSize > event.Message.BufferSize) + { + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Ignoring invalid RPC {}::{} data for object {} (data size: {}, bytes left: {})", String(rpcTypeName), String(rpcName), item.ToString(), msgDataPayload.DataSize, (int32)event.Message.BufferSize - (int32)event.Message.Position); + return; + } InvokeObjectRpc(info, event.Message.Buffer + event.Message.Position, msgDataPayload.DataSize, senderClientId, obj); } else { // Add to RPC from multiple parts PartsItem* rpcItem = AddObjectRpcItem(event, msgData.OwnerFrame, msgDataPayload.PartsCount, msgDataPayload.DataSize, objectId, 0, msgDataPayload.PartSize, senderClientId); - rpcItem->Object = e->Object; - rpcItem->Tag = info; + if (rpcItem) + { + rpcItem->Object = e->Object; + rpcItem->Tag = info; + } } } else if (info->Channel != static_cast(NetworkChannelType::Unreliable) && info->Channel != static_cast(NetworkChannelType::UnreliableOrdered)) @@ -2497,7 +2536,7 @@ void NetworkInternal::OnNetworkMessageObjectRpcPart(NetworkEvent& event, Network } // Remove item - int32 partIndex = (int32)((RpcParts.Get() - rpcItem) / sizeof(rpcItem)); + int32 partIndex = (int32)(RpcParts.Get() - rpcItem); RpcParts.RemoveAt(partIndex); } }