mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-03 16:03:52 +00:00
Add packet size validation to SMSG_GAMEOBJECT_QUERY_RESPONSE parsing
Improve robustness of game object 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, type, displayId fields, and improved in-loop handling for variable-length data array with partial data graceful degradation - Classic (packet_parsers_classic.cpp): Add upfront validation for entry, type, displayId fields, and enhanced in-loop data array read with truncation detection - Both variants now log warnings when data fields are truncated Part of ongoing Tier 2 work to improve multi-expansion packet parsing robustness against malformed or truncated server packets.
This commit is contained in:
parent
d1414b6a46
commit
73abbc2a08
2 changed files with 44 additions and 0 deletions
|
|
@ -979,6 +979,12 @@ bool ClassicPacketParsers::parseGuildQueryResponse(network::Packet& packet, Guil
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
bool ClassicPacketParsers::parseGameObjectQueryResponse(network::Packet& packet, GameObjectQueryResponseData& data) {
|
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();
|
data.entry = packet.readUInt32();
|
||||||
|
|
||||||
// High bit set means gameobject not found
|
// High bit set means gameobject not found
|
||||||
|
|
@ -988,6 +994,12 @@ bool ClassicPacketParsers::parseGameObjectQueryResponse(network::Packet& packet,
|
||||||
return true;
|
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.type = packet.readUInt32();
|
||||||
data.displayId = packet.readUInt32();
|
data.displayId = packet.readUInt32();
|
||||||
// 4 name strings
|
// 4 name strings
|
||||||
|
|
@ -1003,6 +1015,16 @@ bool ClassicPacketParsers::parseGameObjectQueryResponse(network::Packet& packet,
|
||||||
data.data[i] = packet.readUInt32();
|
data.data[i] = packet.readUInt32();
|
||||||
}
|
}
|
||||||
data.hasData = true;
|
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
|
if (data.type == 15) { // MO_TRANSPORT
|
||||||
|
|
|
||||||
|
|
@ -2336,6 +2336,12 @@ network::Packet GameObjectQueryPacket::build(uint32_t entry, uint64_t guid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameObjectQueryResponseParser::parse(network::Packet& packet, GameObjectQueryResponseData& data) {
|
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();
|
data.entry = packet.readUInt32();
|
||||||
|
|
||||||
// High bit set means gameobject not found
|
// High bit set means gameobject not found
|
||||||
|
|
@ -2346,6 +2352,12 @@ bool GameObjectQueryResponseParser::parse(network::Packet& packet, GameObjectQue
|
||||||
return true;
|
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.type = packet.readUInt32(); // GameObjectType
|
||||||
data.displayId = packet.readUInt32();
|
data.displayId = packet.readUInt32();
|
||||||
// 4 name strings (only first is usually populated)
|
// 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.data[i] = packet.readUInt32();
|
||||||
}
|
}
|
||||||
data.hasData = true;
|
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, ")");
|
LOG_DEBUG("GameObject query response: ", data.name, " (type=", data.type, " entry=", data.entry, ")");
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue