From 7034bc5f6391122e7b0b40504e9a94dda75203aa Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 11 Mar 2026 14:29:37 -0700 Subject: [PATCH] Cap hit/miss counts in Classic and TBC spell parsers Add DoS protection to Classic and TBC parseSpellGo implementations: - Cap hitCount and missCount to 128 each (prevents OOM from huge arrays) - Track actual reads vs expected counts - Log truncation warnings with index information - Graceful truncation with count updates Ensures consistent hardening across all expansion variants (Vanilla/TBC/WotLK). --- src/game/packet_parsers_classic.cpp | 22 ++++++++++++++++++++++ src/game/packet_parsers_tbc.cpp | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/game/packet_parsers_classic.cpp b/src/game/packet_parsers_classic.cpp index e4bb12e2..c62567ef 100644 --- a/src/game/packet_parsers_classic.cpp +++ b/src/game/packet_parsers_classic.cpp @@ -391,14 +391,30 @@ bool ClassicPacketParsers::parseSpellGo(network::Packet& packet, SpellGoData& da // Hit targets if (rem() < 1) return true; data.hitCount = packet.readUInt8(); + // Cap hit count to prevent OOM from huge target lists + if (data.hitCount > 128) { + LOG_WARNING("[Classic] Spell go: hitCount capped (requested=", (int)data.hitCount, ")"); + data.hitCount = 128; + } data.hitTargets.reserve(data.hitCount); for (uint8_t i = 0; i < data.hitCount && rem() >= 1; ++i) { data.hitTargets.push_back(UpdateObjectParser::readPackedGuid(packet)); } + // Check if we read all expected hits + if (data.hitTargets.size() < data.hitCount) { + LOG_WARNING("[Classic] Spell go: truncated hit targets at index ", (int)data.hitTargets.size(), + "/", (int)data.hitCount); + data.hitCount = data.hitTargets.size(); + } // Miss targets if (rem() < 1) return true; data.missCount = packet.readUInt8(); + // Cap miss count to prevent OOM + if (data.missCount > 128) { + LOG_WARNING("[Classic] Spell go: missCount capped (requested=", (int)data.missCount, ")"); + data.missCount = 128; + } data.missTargets.reserve(data.missCount); for (uint8_t i = 0; i < data.missCount && rem() >= 2; ++i) { SpellGoMissEntry m; @@ -407,6 +423,12 @@ bool ClassicPacketParsers::parseSpellGo(network::Packet& packet, SpellGoData& da m.missType = packet.readUInt8(); data.missTargets.push_back(m); } + // Check if we read all expected misses + if (data.missTargets.size() < data.missCount) { + LOG_WARNING("[Classic] Spell go: truncated miss targets at index ", (int)data.missTargets.size(), + "/", (int)data.missCount); + data.missCount = data.missTargets.size(); + } LOG_DEBUG("[Classic] Spell go: spell=", data.spellId, " hits=", (int)data.hitCount, " misses=", (int)data.missCount); diff --git a/src/game/packet_parsers_tbc.cpp b/src/game/packet_parsers_tbc.cpp index 767a49ee..ffc462ad 100644 --- a/src/game/packet_parsers_tbc.cpp +++ b/src/game/packet_parsers_tbc.cpp @@ -1276,13 +1276,29 @@ bool TbcPacketParsers::parseSpellGo(network::Packet& packet, SpellGoData& data) } data.hitCount = packet.readUInt8(); + // Cap hit count to prevent OOM from huge target lists + if (data.hitCount > 128) { + LOG_WARNING("[TBC] Spell go: hitCount capped (requested=", (int)data.hitCount, ")"); + data.hitCount = 128; + } data.hitTargets.reserve(data.hitCount); for (uint8_t i = 0; i < data.hitCount && packet.getReadPos() + 8 <= packet.getSize(); ++i) { data.hitTargets.push_back(packet.readUInt64()); // full GUID in TBC } + // Check if we read all expected hits + if (data.hitTargets.size() < data.hitCount) { + LOG_WARNING("[TBC] Spell go: truncated hit targets at index ", (int)data.hitTargets.size(), + "/", (int)data.hitCount); + data.hitCount = data.hitTargets.size(); + } if (packet.getReadPos() < packet.getSize()) { data.missCount = packet.readUInt8(); + // Cap miss count to prevent OOM + if (data.missCount > 128) { + LOG_WARNING("[TBC] Spell go: missCount capped (requested=", (int)data.missCount, ")"); + data.missCount = 128; + } data.missTargets.reserve(data.missCount); for (uint8_t i = 0; i < data.missCount && packet.getReadPos() + 9 <= packet.getSize(); ++i) { SpellGoMissEntry m; @@ -1290,6 +1306,12 @@ bool TbcPacketParsers::parseSpellGo(network::Packet& packet, SpellGoData& data) m.missType = packet.readUInt8(); data.missTargets.push_back(m); } + // Check if we read all expected misses + if (data.missTargets.size() < data.missCount) { + LOG_WARNING("[TBC] Spell go: truncated miss targets at index ", (int)data.missTargets.size(), + "/", (int)data.missCount); + data.missCount = data.missTargets.size(); + } } LOG_DEBUG("[TBC] Spell go: spell=", data.spellId, " hits=", (int)data.hitCount,