From 98739c16103cedcd067b19d0664adb9ac20247cb Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 11 Mar 2026 14:27:39 -0700 Subject: [PATCH] Harden NameQueryResponseParser against malformed packets Add upfront and in-loop validation for the WotLK variant of name query responses: - Validate packed GUID and found flag reads (minimum 2 bytes) - Validate strings can be read before attempting parse - Validate 3 final uint8 fields (race/gender/class) exist before reading - Graceful truncation handling with field initialization Prevents undefined behavior from servers sending truncated/malformed packets. --- src/game/world_packets.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index 0d43a4e2..f12f7fa1 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -2267,7 +2267,17 @@ network::Packet NameQueryPacket::build(uint64_t playerGuid) { bool NameQueryResponseParser::parse(network::Packet& packet, NameQueryResponseData& data) { // 3.3.5a: packedGuid, uint8 found // If found==0: CString name, CString realmName, uint8 race, uint8 gender, uint8 classId + // Validation: packed GUID (1-8 bytes) + found flag (1 byte minimum) + if (packet.getSize() - packet.getReadPos() < 2) return false; // At least 1 for packed GUID + 1 for found + + size_t startPos = packet.getReadPos(); data.guid = UpdateObjectParser::readPackedGuid(packet); + + // Validate found flag read + if (packet.getSize() - packet.getReadPos() < 1) { + packet.setReadPos(startPos); + return false; + } data.found = packet.readUInt8(); if (data.found != 0) { @@ -2275,8 +2285,25 @@ bool NameQueryResponseParser::parse(network::Packet& packet, NameQueryResponseDa return true; // Valid response, just not found } + // Validate strings: need at least 2 null terminators for empty strings + if (packet.getSize() - packet.getReadPos() < 2) { + data.name.clear(); + data.realmName.clear(); + return !data.name.empty(); // Fail if name was required + } + data.name = packet.readString(); data.realmName = packet.readString(); + + // Validate final 3 uint8 fields (race, gender, classId) + if (packet.getSize() - packet.getReadPos() < 3) { + LOG_WARNING("Name query: truncated fields after realmName, expected 3 uint8s"); + data.race = 0; + data.gender = 0; + data.classId = 0; + return !data.name.empty(); + } + data.race = packet.readUInt8(); data.gender = packet.readUInt8(); data.classId = packet.readUInt8();