diff --git a/src/game/packet_parsers_classic.cpp b/src/game/packet_parsers_classic.cpp index c96b06e1..17ff66ce 100644 --- a/src/game/packet_parsers_classic.cpp +++ b/src/game/packet_parsers_classic.cpp @@ -979,6 +979,12 @@ bool ClassicPacketParsers::parseGuildQueryResponse(network::Packet& packet, Guil // ============================================================================ bool ClassicPacketParsers::parseGameObjectQueryResponse(network::Packet& packet, GameObjectQueryResponseData& data) { + // Validate minimum packet size: entry(4) + if (packet.getSize() < 4) { + LOG_ERROR("Classic SMSG_GAMEOBJECT_QUERY_RESPONSE: packet too small (", packet.getSize(), " bytes)"); + return false; + } + data.entry = packet.readUInt32(); // High bit set means gameobject not found @@ -988,6 +994,12 @@ bool ClassicPacketParsers::parseGameObjectQueryResponse(network::Packet& packet, return true; } + // Validate minimum size for fixed fields: type(4) + displayId(4) + if (packet.getSize() - packet.getReadPos() < 8) { + LOG_ERROR("Classic SMSG_GAMEOBJECT_QUERY_RESPONSE: truncated before names (entry=", data.entry, ")"); + return false; + } + data.type = packet.readUInt32(); data.displayId = packet.readUInt32(); // 4 name strings @@ -1003,6 +1015,16 @@ bool ClassicPacketParsers::parseGameObjectQueryResponse(network::Packet& packet, data.data[i] = packet.readUInt32(); } data.hasData = true; + } else if (remaining > 0) { + // Partial data field; read what we can + uint32_t fieldsToRead = remaining / 4; + for (uint32_t i = 0; i < fieldsToRead && i < 24; i++) { + data.data[i] = packet.readUInt32(); + } + if (fieldsToRead < 24) { + LOG_WARNING("Classic SMSG_GAMEOBJECT_QUERY_RESPONSE: truncated in data fields (", fieldsToRead, + " of 24 read, entry=", data.entry, ")"); + } } if (data.type == 15) { // MO_TRANSPORT diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index b437c386..ba6742fc 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -2336,6 +2336,12 @@ network::Packet GameObjectQueryPacket::build(uint32_t entry, uint64_t guid) { } bool GameObjectQueryResponseParser::parse(network::Packet& packet, GameObjectQueryResponseData& data) { + // Validate minimum packet size: entry(4) + if (packet.getSize() < 4) { + LOG_ERROR("SMSG_GAMEOBJECT_QUERY_RESPONSE: packet too small (", packet.getSize(), " bytes)"); + return false; + } + data.entry = packet.readUInt32(); // High bit set means gameobject not found @@ -2346,6 +2352,12 @@ bool GameObjectQueryResponseParser::parse(network::Packet& packet, GameObjectQue return true; } + // Validate minimum size for fixed fields: type(4) + displayId(4) + if (packet.getSize() - packet.getReadPos() < 8) { + LOG_ERROR("SMSG_GAMEOBJECT_QUERY_RESPONSE: truncated before names (entry=", data.entry, ")"); + return false; + } + data.type = packet.readUInt32(); // GameObjectType data.displayId = packet.readUInt32(); // 4 name strings (only first is usually populated) @@ -2367,6 +2379,16 @@ bool GameObjectQueryResponseParser::parse(network::Packet& packet, GameObjectQue data.data[i] = packet.readUInt32(); } data.hasData = true; + } else if (remaining > 0) { + // Partial data field; read what we can + uint32_t fieldsToRead = remaining / 4; + for (uint32_t i = 0; i < fieldsToRead && i < 24; i++) { + data.data[i] = packet.readUInt32(); + } + if (fieldsToRead < 24) { + LOG_WARNING("SMSG_GAMEOBJECT_QUERY_RESPONSE: truncated in data fields (", fieldsToRead, + " of 24 read, entry=", data.entry, ")"); + } } LOG_DEBUG("GameObject query response: ", data.name, " (type=", data.type, " entry=", data.entry, ")");