From 83a368aa858c3edf37cade5f313f2bd067e39caf Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 14 Mar 2026 14:43:15 -0700 Subject: [PATCH] fix(combatlog): reject spell start packets missing target flags --- src/game/packet_parsers_classic.cpp | 24 ++++++++++++++++++++---- src/game/packet_parsers_tbc.cpp | 23 +++++++++++++++-------- src/game/world_packets.cpp | 26 ++++++++++++++++---------- 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/game/packet_parsers_classic.cpp b/src/game/packet_parsers_classic.cpp index 2a8b5268..74935162 100644 --- a/src/game/packet_parsers_classic.cpp +++ b/src/game/packet_parsers_classic.cpp @@ -355,12 +355,21 @@ network::Packet ClassicPacketParsers::buildUseItem(uint8_t bagIndex, uint8_t slo // + uint16(targetFlags) [+ PackedGuid(unitTarget) if TARGET_FLAG_UNIT] // ============================================================================ bool ClassicPacketParsers::parseSpellStart(network::Packet& packet, SpellStartData& data) { + data = SpellStartData{}; + auto rem = [&]() { return packet.getSize() - packet.getReadPos(); }; + const size_t startPos = packet.getReadPos(); if (rem() < 2) return false; - if (!hasFullPackedGuid(packet)) return false; + if (!hasFullPackedGuid(packet)) { + packet.setReadPos(startPos); + return false; + } data.casterGuid = UpdateObjectParser::readPackedGuid(packet); - if (!hasFullPackedGuid(packet)) return false; + if (!hasFullPackedGuid(packet)) { + packet.setReadPos(startPos); + return false; + } data.casterUnit = UpdateObjectParser::readPackedGuid(packet); // uint8 castCount + uint32 spellId + uint16 castFlags + uint32 castTime = 11 bytes @@ -371,11 +380,18 @@ bool ClassicPacketParsers::parseSpellStart(network::Packet& packet, SpellStartDa data.castTime = packet.readUInt32(); // SpellCastTargets: uint16 targetFlags in Vanilla (uint32 in TBC/WotLK) - if (rem() < 2) return true; + if (rem() < 2) { + LOG_WARNING("[Classic] Spell start: missing targetFlags"); + packet.setReadPos(startPos); + return false; + } uint16_t targetFlags = packet.readUInt16(); // TARGET_FLAG_UNIT (0x02) or TARGET_FLAG_OBJECT (0x800) carry a packed GUID if ((targetFlags & 0x02) || (targetFlags & 0x800)) { - if (!hasFullPackedGuid(packet)) return false; + if (!hasFullPackedGuid(packet)) { + packet.setReadPos(startPos); + return false; + } data.targetGuid = UpdateObjectParser::readPackedGuid(packet); } diff --git a/src/game/packet_parsers_tbc.cpp b/src/game/packet_parsers_tbc.cpp index 1343b4c5..d22b0584 100644 --- a/src/game/packet_parsers_tbc.cpp +++ b/src/game/packet_parsers_tbc.cpp @@ -1234,6 +1234,8 @@ bool TbcPacketParsers::parseMailList(network::Packet& packet, std::vector packet.getSize()) { - return false; - } - data.targetGuid = packet.readUInt64(); // full GUID in TBC + if (packet.getReadPos() + 4 > packet.getSize()) { + LOG_WARNING("[TBC] Spell start: missing targetFlags"); + packet.setReadPos(startPos); + return false; + } + + uint32_t targetFlags = packet.readUInt32(); + const bool needsTargetGuid = (targetFlags & 0x02) || (targetFlags & 0x800); // UNIT/OBJECT + if (needsTargetGuid) { + if (packet.getReadPos() + 8 > packet.getSize()) { + packet.setReadPos(startPos); + return false; } + data.targetGuid = packet.readUInt64(); // full GUID in TBC } LOG_DEBUG("[TBC] Spell start: spell=", data.spellId, " castTime=", data.castTime, "ms"); diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index b8e3ad95..f5da2e5f 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -3703,6 +3703,8 @@ bool CastFailedParser::parse(network::Packet& packet, CastFailedData& data) { } bool SpellStartParser::parse(network::Packet& packet, SpellStartData& data) { + data = SpellStartData{}; + // Packed GUIDs are variable-length; only require minimal packet shape up front: // two GUID masks + castCount(1) + spellId(4) + castFlags(4) + castTime(4). if (packet.getSize() - packet.getReadPos() < 15) return false; @@ -3729,17 +3731,21 @@ bool SpellStartParser::parse(network::Packet& packet, SpellStartData& data) { data.castFlags = packet.readUInt32(); data.castTime = packet.readUInt32(); - // Read target flags and target (simplified) - if (packet.getSize() - packet.getReadPos() >= 4) { - uint32_t targetFlags = packet.readUInt32(); - const bool needsTargetGuid = (targetFlags & 0x02) || (targetFlags & 0x800); // UNIT/OBJECT - if (needsTargetGuid) { - if (!hasFullPackedGuid(packet)) { - packet.setReadPos(startPos); - return false; - } - data.targetGuid = UpdateObjectParser::readPackedGuid(packet); + // SpellCastTargets starts with target flags and is mandatory. + if (packet.getSize() - packet.getReadPos() < 4) { + LOG_WARNING("Spell start: missing targetFlags"); + packet.setReadPos(startPos); + return false; + } + + uint32_t targetFlags = packet.readUInt32(); + const bool needsTargetGuid = (targetFlags & 0x02) || (targetFlags & 0x800); // UNIT/OBJECT + if (needsTargetGuid) { + if (!hasFullPackedGuid(packet)) { + packet.setReadPos(startPos); + return false; } + data.targetGuid = UpdateObjectParser::readPackedGuid(packet); } LOG_DEBUG("Spell start: spell=", data.spellId, " castTime=", data.castTime, "ms");