mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
fix: harden Turtle movement block parser with bounds checks
The Turtle parseMovementBlock had no bounds checking on any reads. Since Packet::readUInt8() returns 0 past the end without failing, the parser could "succeed" with all-zero garbage data, then subsequent parseUpdateFields would read from wrong positions, producing "truncated field value" and "truncated update mask" errors. Added bounds checks before every conditional read section (transport, swimming pitch, fall time, jumping, spline elevation, speeds, spline data, tail flags). Also removed the WotLK movement block fallback from the Turtle parser chain — WotLK format is fundamentally incompatible (uint16 flags, 9 speeds) and false-positive parses corrupt NPC data. Also changed spline pointCount > 256 from cap-to-zero to return false so the parser correctly fails instead of silently dropping waypoints.
This commit is contained in:
parent
ce3caf0438
commit
0a04a00234
2 changed files with 32 additions and 9 deletions
|
|
@ -1918,6 +1918,9 @@ namespace TurtleMoveFlags {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TurtlePacketParsers::parseMovementBlock(network::Packet& packet, UpdateBlock& block) {
|
bool TurtlePacketParsers::parseMovementBlock(network::Packet& packet, UpdateBlock& block) {
|
||||||
|
auto rem = [&]() -> size_t { return packet.getSize() - packet.getReadPos(); };
|
||||||
|
if (rem() < 1) return false;
|
||||||
|
|
||||||
uint8_t updateFlags = packet.readUInt8();
|
uint8_t updateFlags = packet.readUInt8();
|
||||||
block.updateFlags = static_cast<uint16_t>(updateFlags);
|
block.updateFlags = static_cast<uint16_t>(updateFlags);
|
||||||
|
|
||||||
|
|
@ -1931,6 +1934,8 @@ bool TurtlePacketParsers::parseMovementBlock(network::Packet& packet, UpdateBloc
|
||||||
const uint8_t UPDATEFLAG_HAS_POSITION = 0x40;
|
const uint8_t UPDATEFLAG_HAS_POSITION = 0x40;
|
||||||
|
|
||||||
if (updateFlags & UPDATEFLAG_LIVING) {
|
if (updateFlags & UPDATEFLAG_LIVING) {
|
||||||
|
// Minimum: moveFlags(4)+time(4)+position(16)+fallTime(4)+speeds(24) = 52 bytes
|
||||||
|
if (rem() < 52) return false;
|
||||||
size_t livingStart = packet.getReadPos();
|
size_t livingStart = packet.getReadPos();
|
||||||
|
|
||||||
uint32_t moveFlags = packet.readUInt32();
|
uint32_t moveFlags = packet.readUInt32();
|
||||||
|
|
@ -1949,8 +1954,10 @@ bool TurtlePacketParsers::parseMovementBlock(network::Packet& packet, UpdateBloc
|
||||||
|
|
||||||
// Transport — Classic flag position 0x02000000
|
// Transport — Classic flag position 0x02000000
|
||||||
if (moveFlags & TurtleMoveFlags::ONTRANSPORT) {
|
if (moveFlags & TurtleMoveFlags::ONTRANSPORT) {
|
||||||
|
if (rem() < 1) return false; // PackedGuid mask byte
|
||||||
block.onTransport = true;
|
block.onTransport = true;
|
||||||
block.transportGuid = UpdateObjectParser::readPackedGuid(packet);
|
block.transportGuid = UpdateObjectParser::readPackedGuid(packet);
|
||||||
|
if (rem() < 20) return false; // 4 floats + u32 timestamp
|
||||||
block.transportX = packet.readFloat();
|
block.transportX = packet.readFloat();
|
||||||
block.transportY = packet.readFloat();
|
block.transportY = packet.readFloat();
|
||||||
block.transportZ = packet.readFloat();
|
block.transportZ = packet.readFloat();
|
||||||
|
|
@ -1960,14 +1967,17 @@ bool TurtlePacketParsers::parseMovementBlock(network::Packet& packet, UpdateBloc
|
||||||
|
|
||||||
// Pitch (swimming only, Classic-style)
|
// Pitch (swimming only, Classic-style)
|
||||||
if (moveFlags & TurtleMoveFlags::SWIMMING) {
|
if (moveFlags & TurtleMoveFlags::SWIMMING) {
|
||||||
|
if (rem() < 4) return false;
|
||||||
/*float pitch =*/ packet.readFloat();
|
/*float pitch =*/ packet.readFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall time (always present)
|
// Fall time (always present)
|
||||||
|
if (rem() < 4) return false;
|
||||||
/*uint32_t fallTime =*/ packet.readUInt32();
|
/*uint32_t fallTime =*/ packet.readUInt32();
|
||||||
|
|
||||||
// Jump data
|
// Jump data
|
||||||
if (moveFlags & TurtleMoveFlags::JUMPING) {
|
if (moveFlags & TurtleMoveFlags::JUMPING) {
|
||||||
|
if (rem() < 16) return false;
|
||||||
/*float jumpVelocity =*/ packet.readFloat();
|
/*float jumpVelocity =*/ packet.readFloat();
|
||||||
/*float jumpSinAngle =*/ packet.readFloat();
|
/*float jumpSinAngle =*/ packet.readFloat();
|
||||||
/*float jumpCosAngle =*/ packet.readFloat();
|
/*float jumpCosAngle =*/ packet.readFloat();
|
||||||
|
|
@ -1976,10 +1986,12 @@ bool TurtlePacketParsers::parseMovementBlock(network::Packet& packet, UpdateBloc
|
||||||
|
|
||||||
// Spline elevation
|
// Spline elevation
|
||||||
if (moveFlags & TurtleMoveFlags::SPLINE_ELEVATION) {
|
if (moveFlags & TurtleMoveFlags::SPLINE_ELEVATION) {
|
||||||
|
if (rem() < 4) return false;
|
||||||
/*float splineElevation =*/ packet.readFloat();
|
/*float splineElevation =*/ packet.readFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turtle: 6 speeds (same as Classic — no flight speeds)
|
// Turtle: 6 speeds (same as Classic — no flight speeds)
|
||||||
|
if (rem() < 24) return false; // 6 × float
|
||||||
float walkSpeed = packet.readFloat();
|
float walkSpeed = packet.readFloat();
|
||||||
float runSpeed = packet.readFloat();
|
float runSpeed = packet.readFloat();
|
||||||
float runBackSpeed = packet.readFloat();
|
float runBackSpeed = packet.readFloat();
|
||||||
|
|
@ -1997,17 +2009,23 @@ bool TurtlePacketParsers::parseMovementBlock(network::Packet& packet, UpdateBloc
|
||||||
bool hasSpline = (moveFlags & TurtleMoveFlags::SPLINE_CLASSIC) ||
|
bool hasSpline = (moveFlags & TurtleMoveFlags::SPLINE_CLASSIC) ||
|
||||||
(moveFlags & TurtleMoveFlags::SPLINE_TBC);
|
(moveFlags & TurtleMoveFlags::SPLINE_TBC);
|
||||||
if (hasSpline) {
|
if (hasSpline) {
|
||||||
|
if (rem() < 4) return false;
|
||||||
uint32_t splineFlags = packet.readUInt32();
|
uint32_t splineFlags = packet.readUInt32();
|
||||||
LOG_DEBUG(" [Turtle] Spline: flags=0x", std::hex, splineFlags, std::dec);
|
LOG_DEBUG(" [Turtle] Spline: flags=0x", std::hex, splineFlags, std::dec);
|
||||||
|
|
||||||
if (splineFlags & 0x00010000) {
|
if (splineFlags & 0x00010000) {
|
||||||
|
if (rem() < 12) return false;
|
||||||
packet.readFloat(); packet.readFloat(); packet.readFloat();
|
packet.readFloat(); packet.readFloat(); packet.readFloat();
|
||||||
} else if (splineFlags & 0x00020000) {
|
} else if (splineFlags & 0x00020000) {
|
||||||
|
if (rem() < 8) return false;
|
||||||
packet.readUInt64();
|
packet.readUInt64();
|
||||||
} else if (splineFlags & 0x00040000) {
|
} else if (splineFlags & 0x00040000) {
|
||||||
|
if (rem() < 4) return false;
|
||||||
packet.readFloat();
|
packet.readFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// timePassed + duration + splineId + pointCount = 16 bytes
|
||||||
|
if (rem() < 16) return false;
|
||||||
/*uint32_t timePassed =*/ packet.readUInt32();
|
/*uint32_t timePassed =*/ packet.readUInt32();
|
||||||
/*uint32_t duration =*/ packet.readUInt32();
|
/*uint32_t duration =*/ packet.readUInt32();
|
||||||
/*uint32_t splineId =*/ packet.readUInt32();
|
/*uint32_t splineId =*/ packet.readUInt32();
|
||||||
|
|
@ -2018,10 +2036,12 @@ bool TurtlePacketParsers::parseMovementBlock(network::Packet& packet, UpdateBloc
|
||||||
++badTurtleSplineCount;
|
++badTurtleSplineCount;
|
||||||
if (badTurtleSplineCount <= 5 || (badTurtleSplineCount % 100) == 0) {
|
if (badTurtleSplineCount <= 5 || (badTurtleSplineCount % 100) == 0) {
|
||||||
LOG_WARNING(" [Turtle] Spline pointCount=", pointCount,
|
LOG_WARNING(" [Turtle] Spline pointCount=", pointCount,
|
||||||
" exceeds max, capping (occurrence=", badTurtleSplineCount, ")");
|
" exceeds max (occurrence=", badTurtleSplineCount, ")");
|
||||||
}
|
}
|
||||||
pointCount = 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
// points + endPoint
|
||||||
|
if (rem() < static_cast<size_t>(pointCount) * 12 + 12) return false;
|
||||||
for (uint32_t i = 0; i < pointCount; i++) {
|
for (uint32_t i = 0; i < pointCount; i++) {
|
||||||
packet.readFloat(); packet.readFloat(); packet.readFloat();
|
packet.readFloat(); packet.readFloat(); packet.readFloat();
|
||||||
}
|
}
|
||||||
|
|
@ -2034,6 +2054,7 @@ bool TurtlePacketParsers::parseMovementBlock(network::Packet& packet, UpdateBloc
|
||||||
" bytes, readPos now=", packet.getReadPos());
|
" bytes, readPos now=", packet.getReadPos());
|
||||||
}
|
}
|
||||||
else if (updateFlags & UPDATEFLAG_HAS_POSITION) {
|
else if (updateFlags & UPDATEFLAG_HAS_POSITION) {
|
||||||
|
if (rem() < 16) return false;
|
||||||
block.x = packet.readFloat();
|
block.x = packet.readFloat();
|
||||||
block.y = packet.readFloat();
|
block.y = packet.readFloat();
|
||||||
block.z = packet.readFloat();
|
block.z = packet.readFloat();
|
||||||
|
|
@ -2045,18 +2066,22 @@ bool TurtlePacketParsers::parseMovementBlock(network::Packet& packet, UpdateBloc
|
||||||
|
|
||||||
// High GUID — 1×u32
|
// High GUID — 1×u32
|
||||||
if (updateFlags & UPDATEFLAG_HIGHGUID) {
|
if (updateFlags & UPDATEFLAG_HIGHGUID) {
|
||||||
|
if (rem() < 4) return false;
|
||||||
/*uint32_t highGuid =*/ packet.readUInt32();
|
/*uint32_t highGuid =*/ packet.readUInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateFlags & UPDATEFLAG_ALL) {
|
if (updateFlags & UPDATEFLAG_ALL) {
|
||||||
|
if (rem() < 4) return false;
|
||||||
/*uint32_t unkAll =*/ packet.readUInt32();
|
/*uint32_t unkAll =*/ packet.readUInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateFlags & UPDATEFLAG_MELEE_ATTACKING) {
|
if (updateFlags & UPDATEFLAG_MELEE_ATTACKING) {
|
||||||
|
if (rem() < 1) return false;
|
||||||
/*uint64_t meleeTargetGuid =*/ UpdateObjectParser::readPackedGuid(packet);
|
/*uint64_t meleeTargetGuid =*/ UpdateObjectParser::readPackedGuid(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateFlags & UPDATEFLAG_TRANSPORT) {
|
if (updateFlags & UPDATEFLAG_TRANSPORT) {
|
||||||
|
if (rem() < 4) return false;
|
||||||
/*uint32_t transportTime =*/ packet.readUInt32();
|
/*uint32_t transportTime =*/ packet.readUInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2185,12 +2210,10 @@ bool TurtlePacketParsers::parseUpdateObject(network::Packet& packet, UpdateObjec
|
||||||
return this->TbcPacketParsers::parseMovementBlock(p, b);
|
return this->TbcPacketParsers::parseMovementBlock(p, b);
|
||||||
}, "tbc");
|
}, "tbc");
|
||||||
}
|
}
|
||||||
if (!ok) {
|
// NOTE: Do NOT fall back to WotLK parseMovementBlock here.
|
||||||
ok = parseMovementVariant(
|
// WotLK uses uint16 updateFlags and 9 speeds vs Classic's uint8
|
||||||
[](network::Packet& p, UpdateBlock& b) {
|
// and 6 speeds. A false-positive WotLK parse consumes wrong bytes,
|
||||||
return UpdateObjectParser::parseMovementBlock(p, b);
|
// corrupting subsequent update fields and losing NPC data.
|
||||||
}, "wotlk");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case UpdateType::OUT_OF_RANGE_OBJECTS:
|
case UpdateType::OUT_OF_RANGE_OBJECTS:
|
||||||
case UpdateType::NEAR_OBJECTS:
|
case UpdateType::NEAR_OBJECTS:
|
||||||
|
|
|
||||||
|
|
@ -1024,7 +1024,7 @@ bool UpdateObjectParser::parseMovementBlock(network::Packet& packet, UpdateBlock
|
||||||
/*float splineElevation =*/ packet.readFloat();
|
/*float splineElevation =*/ packet.readFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Speeds (7 speed values)
|
// Speeds (9 values in WotLK: walk/run/runBack/swim/swimBack/flight/flightBack/turn/pitch)
|
||||||
/*float walkSpeed =*/ packet.readFloat();
|
/*float walkSpeed =*/ packet.readFloat();
|
||||||
float runSpeed = packet.readFloat();
|
float runSpeed = packet.readFloat();
|
||||||
/*float runBackSpeed =*/ packet.readFloat();
|
/*float runBackSpeed =*/ packet.readFloat();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue