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.
This commit is contained in:
Kelsi 2026-04-03 19:36:34 -07:00
parent 40e72d535e
commit def821055b
2 changed files with 29 additions and 10 deletions

View file

@ -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<uint8_t>(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));

View file

@ -7,6 +7,7 @@
#include <algorithm>
#include <array>
#include <cctype>
#include <cmath>
#include <cstring>
#include <sstream>
#include <iomanip>
@ -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;
};