diff --git a/src/pipeline/wowee_building.cpp b/src/pipeline/wowee_building.cpp index 14e405ca..18d76595 100644 --- a/src/pipeline/wowee_building.cpp +++ b/src/pipeline/wowee_building.cpp @@ -31,9 +31,18 @@ WoweeBuilding WoweeBuildingLoader::load(const std::string& basePath) { f.read(reinterpret_cast(&portalCount), 4); f.read(reinterpret_cast(&doodadCount), 4); f.read(reinterpret_cast(&bld.boundRadius), 4); + // Sanity bounds. Real WMOs are usually 1-50 groups; >4096 indicates a + // corrupted header that would OOM us when we resize() vectors. + if (groupCount > 4096 || portalCount > 8192 || doodadCount > 65536) { + LOG_ERROR("WOB header rejected (groups=", groupCount, + " portals=", portalCount, " doodads=", doodadCount, "): ", basePath); + return WoweeBuilding{}; + } + if (!std::isfinite(bld.boundRadius) || bld.boundRadius < 0.0f) bld.boundRadius = 1.0f; uint16_t nameLen; f.read(reinterpret_cast(&nameLen), 2); + if (nameLen > 1024) nameLen = 0; bld.name.resize(nameLen); f.read(bld.name.data(), nameLen); diff --git a/src/pipeline/wowee_model.cpp b/src/pipeline/wowee_model.cpp index 79d4649d..ba501178 100644 --- a/src/pipeline/wowee_model.cpp +++ b/src/pipeline/wowee_model.cpp @@ -39,6 +39,14 @@ WoweeModel WoweeModelLoader::load(const std::string& basePath) { f.read(reinterpret_cast(&model.boundRadius), 4); f.read(reinterpret_cast(&model.boundMin), 12); f.read(reinterpret_cast(&model.boundMax), 12); + // Sanity bounds. Real M2 models cap at 65k vertices (uint16 indices) and + // typically 64 textures. Reject obviously corrupted counts before we + // try to allocate huge vertex/index buffers. + if (vertCount > 1'000'000 || indexCount > 4'000'000 || texCount > 1024) { + LOG_ERROR("WOM header rejected (verts=", vertCount, + " indices=", indexCount, " textures=", texCount, "): ", basePath); + return WoweeModel{}; + } // Bound sanity — radius drives M2 culling, min/max drive collision AABBs. // NaN/inf would either cull-out the model or crash the cull math.