diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index c6d4001455d..a34a50f2cec 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -1131,7 +1131,7 @@ bool CGame::StaticProcessNetworkPacket(unsigned char ucPacketID, const NetServer { if (ucPacketID == PACKET_ID_LUA_EVENT) { - if (pBitStream->GetNumberOfUnreadBits() > CLuaEventPacket::MAX_LUA_EVENT_ARGUMENTS_SIZE * 8) + if (pBitStream->GetNumberOfUnreadBits() > (CLuaEventPacket::MAX_LUA_EVENT_ARGUMENTS_SIZE + CLuaEventPacket::LUA_EVENT_ENVELOPE_HEADROOM) * 8) return false; } diff --git a/Server/mods/deathmatch/logic/packets/CLuaEventPacket.cpp b/Server/mods/deathmatch/logic/packets/CLuaEventPacket.cpp index c0706f681f3..f56de37a4bc 100644 --- a/Server/mods/deathmatch/logic/packets/CLuaEventPacket.cpp +++ b/Server/mods/deathmatch/logic/packets/CLuaEventPacket.cpp @@ -32,7 +32,7 @@ bool CLuaEventPacket::Read(NetBitStreamInterface& BitStream) { if (usNameLength < (MAX_EVENT_NAME_LENGTH - 1) && BitStream.ReadStringCharacters(m_strName, usNameLength) && BitStream.Read(m_ElementID)) { - if (BitStream.GetNumberOfUnreadBits() > MAX_LUA_LATENT_EVENT_ARGUMENTS_SIZE * 8) + if (BitStream.GetNumberOfUnreadBits() > (MAX_LUA_LATENT_EVENT_ARGUMENTS_SIZE + LUA_EVENT_ENVELOPE_HEADROOM) * 8) return false; // Faster than using a constructor diff --git a/Server/mods/deathmatch/logic/packets/CLuaEventPacket.h b/Server/mods/deathmatch/logic/packets/CLuaEventPacket.h index 281c5334df0..d244f44d8b1 100644 --- a/Server/mods/deathmatch/logic/packets/CLuaEventPacket.h +++ b/Server/mods/deathmatch/logic/packets/CLuaEventPacket.h @@ -17,6 +17,7 @@ class CLuaEventPacket final : public CPacket { public: + static constexpr int LUA_EVENT_ENVELOPE_HEADROOM = 64 * 1024; // Limit for the size of the arguments in a single triggerEvent. (1MB) static constexpr int MAX_LUA_EVENT_ARGUMENTS_SIZE = 1024 * 1024; // Limit for the size of the arguments in a single triggerLatentEvent. (10MB) diff --git a/Shared/mods/deathmatch/logic/CLatentReceiver.cpp b/Shared/mods/deathmatch/logic/CLatentReceiver.cpp index fc2115dbeb9..1acdaa1b64a 100644 --- a/Shared/mods/deathmatch/logic/CLatentReceiver.cpp +++ b/Shared/mods/deathmatch/logic/CLatentReceiver.cpp @@ -128,12 +128,10 @@ void CLatentReceiver::OnReceive(NetBitStreamInterface* pBitStream) if (uiFinalSize > 100 * 1024 * 1024) return OnReceiveError("uiFinalSize too large"); - // CATEGORY_PACKET reassembles raw packet data and feeds it into - // the main packet dispatch (CGame::StaticProcessPacket). Without - // a tighter cap, an attacker can craft a 100MB payload targeting - // packet handlers that scale poorly with input size (e.g. - // CPlayerModInfoPacket). 10MB is well above any legitimate use. - constexpr uint LATENT_PACKET_MAX_SIZE = 10 * 1024 * 1024; + // uiFinalSize includes the 5-byte CATEGORY_PACKET wrapper and the + // serialized event envelope. Keep 10 MiB available for argument data + // while allowing a small amount of protocol overhead. + constexpr uint LATENT_PACKET_MAX_SIZE = 10 * 1024 * 1024 + 64 * 1024; if (usCategory == CATEGORY_PACKET && uiFinalSize > LATENT_PACKET_MAX_SIZE) return OnReceiveError("CATEGORY_PACKET payload too large"); @@ -159,8 +157,10 @@ void CLatentReceiver::OnReceive(NetBitStreamInterface* pBitStream) // // Read body // - if (activeRx.uiWritePosition + usSizeSent > activeRx.buffer.GetSize()) - return OnReceiveError("Buffer would overflow"); + if (activeRx.uiWritePosition > activeRx.buffer.GetSize() || usSizeSent > activeRx.buffer.GetSize() - activeRx.uiWritePosition) + { + return OnReceiveError(SString("Buffer would overflow (size:%u pos:%u chunk:%u)", activeRx.buffer.GetSize(), activeRx.uiWritePosition, usSizeSent)); + } if (bIsTail && activeRx.uiWritePosition + usSizeSent != activeRx.buffer.GetSize()) return OnReceiveError("Buffer size wrong");