From def821055b3b18c576605d8827ed5c946c9f4a09 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Fri, 3 Apr 2026 19:36:34 -0700 Subject: [PATCH] fix(parsing): validate spline endPoint coords to reject false-positive format matches The WotLK spline parser tries 6 format variants and accepts the first that passes minimal validation (pointCount<=256, splineMode<=3). A wrong format can pass by coincidence, consuming incorrect bytes and corrupting all subsequent UPDATE_OBJECT blocks (e.g. maskBlockCount=219 garbage). Add endPoint coordinate validation: reject spline parses where the endpoint is non-finite or outside world bounds (65k). Also harden the Turtle parser to keep successfully-parsed blocks on mid-packet failure instead of discarding the entire packet. --- src/game/packet_parsers_classic.cpp | 24 ++++++++++++++++-------- src/game/world_packets.cpp | 15 +++++++++++++-- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/game/packet_parsers_classic.cpp b/src/game/packet_parsers_classic.cpp index 5b02127b..f757b931 100644 --- a/src/game/packet_parsers_classic.cpp +++ b/src/game/packet_parsers_classic.cpp @@ -2140,6 +2140,8 @@ bool TurtlePacketParsers::parseUpdateObject(network::Packet& packet, UpdateObjec out.blocks.reserve(out.blockCount); for (uint32_t i = 0; i < out.blockCount; ++i) { if (!packet.hasData()) { + // If we already parsed some blocks, keep them (layout is confirmed valid). + if (!out.blocks.empty()) break; packet.setReadPos(start); return false; } @@ -2147,6 +2149,7 @@ bool TurtlePacketParsers::parseUpdateObject(network::Packet& packet, UpdateObjec const size_t blockStart = packet.getReadPos(); uint8_t updateTypeVal = packet.readUInt8(); if (updateTypeVal > static_cast(UpdateType::NEAR_OBJECTS)) { + if (!out.blocks.empty()) break; packet.setReadPos(start); return false; } @@ -2220,14 +2223,19 @@ bool TurtlePacketParsers::parseUpdateObject(network::Packet& packet, UpdateObjec } if (!ok) { - LOG_WARNING("[Turtle] SMSG_UPDATE_OBJECT block parse failed", - " blockIndex=", i, - " updateType=", updateTypeName(updateType), - " readPos=", packet.getReadPos(), - " blockStart=", blockStart, - " packetSize=", packet.getSize()); - packet.setReadPos(start); - return false; + static int turtleBlockErrors = 0; + if (++turtleBlockErrors <= 5) { + LOG_WARNING("[Turtle] SMSG_UPDATE_OBJECT block parse failed", + " blockIndex=", i, " of ", out.blockCount, + " updateType=", updateTypeName(updateType), + " readPos=", packet.getReadPos(), + " blockStart=", blockStart, + " packetSize=", packet.getSize(), + " (", out.blocks.size(), " blocks kept)"); + } + // Keep successfully parsed blocks instead of discarding all. + // Cannot re-sync within the packet, so stop parsing here. + break; } out.blocks.push_back(std::move(block)); diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index 4867b295..8df1dec0 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -1010,8 +1011,18 @@ bool UpdateObjectParser::parseMovementBlock(network::Packet& packet, UpdateBlock packet.setReadPos(prePointCount); return false; } - packet.readFloat(); packet.readFloat(); packet.readFloat(); // endPoint - LOG_DEBUG(" Spline pointCount=", pc, " compressed=", compressed, " (", tag, ")"); + float epX = packet.readFloat(); + float epY = packet.readFloat(); + float epZ = packet.readFloat(); + // Validate endPoint: garbage bytes rarely produce finite world coords + if (!std::isfinite(epX) || !std::isfinite(epY) || !std::isfinite(epZ) || + std::fabs(epX) > 65000.0f || std::fabs(epY) > 65000.0f || + std::fabs(epZ) > 65000.0f) { + packet.setReadPos(prePointCount); + return false; + } + LOG_DEBUG(" Spline pointCount=", pc, " compressed=", compressed, + " endPt=(", epX, ",", epY, ",", epZ, ") (", tag, ")"); return true; };