Add packet size validation to SMSG_CREATURE_QUERY_RESPONSE parsing

Improve robustness of creature query response parsing by adding defensive
size checks to both WotLK/TBC and Classic variants:

- WotLK/TBC (world_packets.cpp): Add upfront validation for entry field,
  validate minimum size (16 bytes) before reading fixed fields
  (typeFlags, creatureType, family, rank), graceful truncation handling
- Classic (packet_parsers_classic.cpp): Add upfront entry validation,
  enhance existing truncation check with default field initialization,
  improve logging consistency
- Both variants now initialize fields to 0 on truncation and log warnings
  with entry context

Part of ongoing Tier 2 work to improve multi-expansion packet parsing robustness
against malformed or truncated server packets.
This commit is contained in:
Kelsi 2026-03-11 14:19:58 -07:00
parent e464300346
commit 2f1b142e14
2 changed files with 31 additions and 3 deletions

View file

@ -1755,6 +1755,12 @@ network::Packet ClassicPacketParsers::buildAcceptQuestPacket(uint64_t npcGuid, u
// ============================================================================
bool ClassicPacketParsers::parseCreatureQueryResponse(network::Packet& packet,
CreatureQueryResponseData& data) {
// Validate minimum packet size: entry(4)
if (packet.getSize() < 4) {
LOG_ERROR("Classic SMSG_CREATURE_QUERY_RESPONSE: packet too small (", packet.getSize(), " bytes)");
return false;
}
data.entry = packet.readUInt32();
if (data.entry & 0x80000000) {
data.entry &= ~0x80000000;
@ -1769,15 +1775,19 @@ bool ClassicPacketParsers::parseCreatureQueryResponse(network::Packet& packet,
data.subName = packet.readString();
// NOTE: NO iconName field in Classic 1.12 — goes straight to typeFlags
if (packet.getReadPos() + 16 > packet.getSize()) {
LOG_WARNING("[Classic] Creature query: truncated at typeFlags (entry=", data.entry, ")");
return true;
LOG_WARNING("Classic SMSG_CREATURE_QUERY_RESPONSE: truncated at typeFlags (entry=", data.entry, ")");
data.typeFlags = 0;
data.creatureType = 0;
data.family = 0;
data.rank = 0;
return true; // Have name/sub fields; base fields are important but optional
}
data.typeFlags = packet.readUInt32();
data.creatureType = packet.readUInt32();
data.family = packet.readUInt32();
data.rank = packet.readUInt32();
LOG_DEBUG("[Classic] Creature query: ", data.name, " type=", data.creatureType,
LOG_DEBUG("Classic SMSG_CREATURE_QUERY_RESPONSE: ", data.name, " type=", data.creatureType,
" rank=", data.rank);
return true;
}

View file

@ -2295,6 +2295,12 @@ network::Packet CreatureQueryPacket::build(uint32_t entry, uint64_t guid) {
}
bool CreatureQueryResponseParser::parse(network::Packet& packet, CreatureQueryResponseData& data) {
// Validate minimum packet size: entry(4)
if (packet.getSize() < 4) {
LOG_ERROR("SMSG_CREATURE_QUERY_RESPONSE: packet too small (", packet.getSize(), " bytes)");
return false;
}
data.entry = packet.readUInt32();
// High bit set means creature not found
@ -2312,6 +2318,18 @@ bool CreatureQueryResponseParser::parse(network::Packet& packet, CreatureQueryRe
packet.readString(); // name4
data.subName = packet.readString();
data.iconName = packet.readString();
// WotLK: 4 fixed fields after iconName (typeFlags, creatureType, family, rank)
// Validate minimum size for these fields: 4×4 = 16 bytes
if (packet.getSize() - packet.getReadPos() < 16) {
LOG_WARNING("SMSG_CREATURE_QUERY_RESPONSE: truncated before typeFlags (entry=", data.entry, ")");
data.typeFlags = 0;
data.creatureType = 0;
data.family = 0;
data.rank = 0;
return true; // Have name/sub/icon; base fields are important but optional
}
data.typeFlags = packet.readUInt32();
data.creatureType = packet.readUInt32();
data.family = packet.readUInt32();