Skip to content

Commit 2ec33b0

Browse files
committed
mdlpp: add boundary checks before seeking to calculated offsets
1 parent 9f36b32 commit 2ec33b0

File tree

1 file changed

+65
-18
lines changed

1 file changed

+65
-18
lines changed

src/mdlpp/structs/MDL.cpp

Lines changed: 65 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,18 @@ constexpr int32_t MDL_ID = parser::binary::makeFourCC("IDST");
1111

1212
namespace {
1313

14+
// boundary check before seeking
15+
bool seekAndValidate(BufferStreamReadOnly& stream, const uint64_t offset) {
16+
if (offset >= stream.size()) {
17+
return false;
18+
}
19+
stream.seek_u(offset);
20+
return true;
21+
}
22+
1423
// TODO: Decompress RLE animation data into frame values
15-
void readAnimValueRLE(BufferStreamReadOnly& stream, int frameCount, std::vector<AnimValue>& outData) {
24+
// TODO: Make lambda return bool for the boundary check?
25+
void readAnimValueRLE(BufferStreamReadOnly& stream, const int frameCount, std::vector<AnimValue>& outData) {
1626
AnimValue val{};
1727
int framesCovered = 0;
1828
while (framesCovered < frameCount) {
@@ -225,7 +235,9 @@ bool MDL::open(const std::byte* data, std::size_t size) {
225235

226236
for (int j = 0; j < hitboxCount; j++) {
227237
const auto hitboxPos = hitboxOffset + j * (sizeof(int32_t) * 11 + sizeof(math::Vec3f) * 2);
228-
stream.seek_u(hitboxSetPos + hitboxPos);
238+
if (!seekAndValidate(stream, hitboxSetPos + hitboxPos)) {
239+
return false;
240+
}
229241

230242
auto& hitbox = hitboxSet.hitboxes.emplace_back();
231243

@@ -289,8 +301,10 @@ bool MDL::open(const std::byte* data, std::size_t size) {
289301
// TODO: Load external animations if animBlock != 0
290302
if (animDesc.animIndex != 0 && animDesc.animBlock == 0) {
291303
const auto animDataPos = animDescPos + animDesc.animIndex;
304+
if (!seekAndValidate(stream, animDataPos)) {
305+
return false;
306+
}
292307
const auto savedPos = stream.tell();
293-
stream.seek_u(animDataPos);
294308

295309
// LL of mstudioanim_t structures
296310
// bone, flags, nextoffset (2 bytes), then var data
@@ -331,7 +345,9 @@ bool MDL::open(const std::byte* data, std::size_t size) {
331345
// pitch/yaw/roll
332346
for (short comp : rotPtr) {
333347
if (comp > 0) {
334-
stream.seek_u(ptrPos - sizeof(AnimValuePtr) + comp);
348+
if (!seekAndValidate(stream, ptrPos - sizeof(AnimValuePtr) + comp)) {
349+
return false;
350+
}
335351
readAnimValueRLE(stream, animDesc.frameCount, boneAnim.animRotationData);
336352
}
337353
}
@@ -350,7 +366,9 @@ bool MDL::open(const std::byte* data, std::size_t size) {
350366
// x/y/z
351367
for (short comp : posPtr) {
352368
if (comp > 0) {
353-
stream.seek_u(ptrPos - sizeof(AnimValuePtr) + comp);
369+
if (!seekAndValidate(stream, ptrPos - sizeof(AnimValuePtr) + comp)) {
370+
return false;
371+
}
354372
readAnimValueRLE(stream, animDesc.frameCount, boneAnim.animPositionData);
355373
}
356374
}
@@ -361,14 +379,18 @@ bool MDL::open(const std::byte* data, std::size_t size) {
361379
if (nextOffset == 0) {
362380
break;
363381
}
364-
stream.seek_u(boneAnimPos + nextOffset);
382+
if (!seekAndValidate(stream, boneAnimPos + nextOffset)) {
383+
return false;
384+
}
365385
}
366386

367387
stream.seek_u(savedPos);
368388
}
369389

370390
const auto movementDataPos = animDescPos + movementIndex;
371-
stream.seek_u(movementDataPos);
391+
if (!seekAndValidate(stream, movementDataPos)) {
392+
return false;
393+
}
372394

373395
for (int j = 0; j < movementCount; j++) {
374396
auto& movement = animDesc.movements.emplace_back();
@@ -386,7 +408,9 @@ bool MDL::open(const std::byte* data, std::size_t size) {
386408

387409
if (ikRuleCount > 0 && ikRuleIndex != 0 && animDesc.animBlock == 0) {
388410
const auto ikRuleDataPos = animDescPos + ikRuleIndex;
389-
stream.seek_u(ikRuleDataPos);
411+
if (!seekAndValidate(stream, ikRuleDataPos)) {
412+
return false;
413+
}
390414

391415
for (int j = 0; j < ikRuleCount; j++) {
392416
const auto ikRulePos = ikRuleDataPos + j * (sizeof(int32_t) * 20 + sizeof(float) * 11 + sizeof(math::Vec3f) + sizeof(math::Quat));
@@ -446,7 +470,9 @@ bool MDL::open(const std::byte* data, std::size_t size) {
446470
const auto compErrorDataPos = stream.tell();
447471
for (short k : ikRule.compressedIKError->offset) {
448472
if (k > 0) {
449-
stream.seek_u(compErrorDataPos + k);
473+
if (!seekAndValidate(stream, compErrorDataPos + k)) {
474+
return false;
475+
}
450476
readAnimValueRLE(stream, animDesc.frameCount, ikRule.compressedIKError->animValues);
451477
}
452478
}
@@ -634,7 +660,9 @@ bool MDL::open(const std::byte* data, std::size_t size) {
634660

635661
for (int j = 0; j < modelsCount; j++) {
636662
auto modelPos = modelsOffset + j * (64 + sizeof(float) + sizeof(int32_t) * 20);
637-
stream.seek_u(bodyPartPos + modelPos);
663+
if (!seekAndValidate(stream, bodyPartPos + modelPos)) {
664+
return false;
665+
}
638666

639667
auto& model = bodyPart.models.emplace_back();
640668

@@ -659,7 +687,9 @@ bool MDL::open(const std::byte* data, std::size_t size) {
659687

660688
for (int k = 0; k < meshesCount; k++) {
661689
const auto meshPos = meshesOffset + k * (sizeof(int32_t) * (18 + MAX_LOD_COUNT) + sizeof(math::Vec3f));
662-
stream.seek_u(bodyPartPos + modelPos + meshPos);
690+
if (!seekAndValidate(stream, bodyPartPos + modelPos + meshPos)) {
691+
return false;
692+
}
663693

664694
auto& mesh = model.meshes.emplace_back();
665695

@@ -686,7 +716,9 @@ bool MDL::open(const std::byte* data, std::size_t size) {
686716

687717
for (int m = 0; m < meshFlexCount; m++) {
688718
const auto flexPos = meshFlexOffset + m * (sizeof(int32_t) * 10 + sizeof(float) * 4 + sizeof(uint8_t) * 4);
689-
stream.seek_u(bodyPartPos + modelPos + meshPos + flexPos);
719+
if (!seekAndValidate(stream, bodyPartPos + modelPos + meshPos + flexPos)) {
720+
return false;
721+
}
690722

691723
auto& flex = mesh.flexes.emplace_back();
692724

@@ -707,7 +739,9 @@ bool MDL::open(const std::byte* data, std::size_t size) {
707739
if (numverts > 0 && vertindex != 0) {
708740
// vertindex is relative to the flex structure base
709741
const auto vertAnimPos = bodyPartPos + modelPos + meshPos + flexPos + vertindex;
710-
stream.seek_u(vertAnimPos);
742+
if (!seekAndValidate(stream, vertAnimPos)) {
743+
return false;
744+
}
711745

712746
if (flex.vertanimtype == 0) {
713747
// normal vertex animations
@@ -743,7 +777,9 @@ bool MDL::open(const std::byte* data, std::size_t size) {
743777
if (eyeballsCount > 0 && eyeballsOffset != 0) {
744778
for (int k = 0; k < eyeballsCount; k++) {
745779
const auto eyeballPos = eyeballsOffset + k * (sizeof(int32_t) * 24 + sizeof(float) * 9 + sizeof(math::Vec3f) * 3 + sizeof(uint8_t) * 4);
746-
stream.seek_u(bodyPartPos + modelPos + eyeballPos);
780+
if (!seekAndValidate(stream, bodyPartPos + modelPos + eyeballPos)) {
781+
return false;
782+
}
747783

748784
auto& eyeball = model.eyeballs.emplace_back();
749785

@@ -779,7 +815,9 @@ bool MDL::open(const std::byte* data, std::size_t size) {
779815
if (modelAttachmentsCount > 0 && modelAttachmentsOffset != 0) {
780816
for (int k = 0; k < modelAttachmentsCount; k++) {
781817
const auto attachmentPos = modelAttachmentsOffset + k * (sizeof(int32_t) + sizeof(uint32_t) + sizeof(int32_t) + sizeof(math::Mat3x4f) + sizeof(int32_t) * 8);
782-
stream.seek_u(bodyPartPos + modelPos + attachmentPos);
818+
if (!seekAndValidate(stream, bodyPartPos + modelPos + attachmentPos)) {
819+
return false;
820+
}
783821

784822
auto& attachment = model.attachments.emplace_back();
785823

@@ -862,7 +900,10 @@ bool MDL::open(const std::byte* data, std::size_t size) {
862900
auto readStringAtRelativeOffset = [&](std::string& target) {
863901
if (const auto offset = stream.read<int32_t>(); offset != 0) {
864902
const auto savedPos = stream.tell();
865-
stream.seek_u(includeModelPos + offset);
903+
// skips if fail
904+
if (!seekAndValidate(stream, includeModelPos + offset)) {
905+
return;
906+
}
866907
stream.read(target);
867908
stream.seek_u(savedPos);
868909
}
@@ -890,7 +931,10 @@ bool MDL::open(const std::byte* data, std::size_t size) {
890931
auto readStringAtRelativeOffset = [&](std::string& target) {
891932
if (const auto offset = stream.read<int32_t>(); offset != 0) {
892933
const auto savedPos = stream.tell();
893-
stream.seek_u(flexControllerPos + offset);
934+
// ditto skip
935+
if (!seekAndValidate(stream, flexControllerPos + offset)) {
936+
return;
937+
}
894938
stream.read(target);
895939
stream.seek_u(savedPos);
896940
}
@@ -1031,7 +1075,10 @@ bool MDL::open(const std::byte* data, std::size_t size) {
10311075
stream.seek_u(flexControllerUIPos + szindex);
10321076
stream.skip<int32_t>();
10331077
if (const auto controllerNameOffset = stream.read<int32_t>(); controllerNameOffset != 0) {
1034-
stream.seek_u(flexControllerUIPos + szindex + controllerNameOffset);
1078+
// skips if fail
1079+
if (!seekAndValidate(stream, flexControllerUIPos + szindex + controllerNameOffset)) {
1080+
return;
1081+
}
10351082
stream.read(targetName);
10361083
}
10371084
stream.seek_u(savedPos);

0 commit comments

Comments
 (0)