From fa1643dc90738c709c5283359da4f1588d27f47a Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sun, 29 Mar 2026 20:32:47 -0700 Subject: [PATCH] fix: WMO readArray integer overflow in bounds check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit count * sizeof(T) was computed in uint32_t — a large count value from a crafted WMO file could wrap to a small number, pass the bounds check, then attempt a multi-GB allocation causing OOM/crash. Now uses 64-bit arithmetic with a 64MB sanity cap, matching the M2 loader pattern. --- src/pipeline/wmo_loader.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/pipeline/wmo_loader.cpp b/src/pipeline/wmo_loader.cpp index 5f919fd8..d466f18d 100644 --- a/src/pipeline/wmo_loader.cpp +++ b/src/pipeline/wmo_loader.cpp @@ -49,11 +49,16 @@ T read(const std::vector& data, uint32_t& offset) { template std::vector readArray(const std::vector& data, uint32_t offset, uint32_t count) { std::vector result; - if (offset + count * sizeof(T) > data.size()) { + // Use 64-bit arithmetic to prevent uint32 overflow on crafted count values. + // A large count (e.g., 0x20000001 with sizeof(T)=8) would wrap to a small + // value in 32-bit, pass the bounds check, then cause a multi-GB allocation. + uint64_t totalBytes = static_cast(count) * sizeof(T); + constexpr uint64_t kMaxReadBytes = 64u * 1024u * 1024u; // 64MB sanity cap + if (totalBytes > kMaxReadBytes || static_cast(offset) + totalBytes > data.size()) { return result; } result.resize(count); - std::memcpy(result.data(), &data[offset], count * sizeof(T)); + std::memcpy(result.data(), &data[offset], static_cast(totalBytes)); return result; }