From fe7912b5faf3be11df1b3787180b647c2577cbf5 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 30 Mar 2026 15:33:03 -0700 Subject: [PATCH] fix: prevent buffer overflows in Warden PE parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add bounds checks to readLE32/readLE16 — malformed Warden modules could cause out-of-bounds reads on untrusted PE data - Fix unsigned underflow in PE section loading: if rawDataOffset or virtualAddr exceeds buffer size, the subtraction wrapped to a huge uint32_t causing memcpy to read/write far beyond bounds. Now skips the section entirely and uses std::min with pre-validated maxima --- src/game/warden_memory.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/game/warden_memory.cpp b/src/game/warden_memory.cpp index d57586bb..2c0b6c67 100644 --- a/src/game/warden_memory.cpp +++ b/src/game/warden_memory.cpp @@ -14,12 +14,16 @@ namespace wowee { namespace game { +// Bounds-checked little-endian reads for PE parsing — malformed Warden modules +// must not cause out-of-bounds access. static inline uint32_t readLE32(const std::vector& data, size_t offset) { + if (offset + 4 > data.size()) return 0; return data[offset] | (uint32_t(data[offset+1]) << 8) | (uint32_t(data[offset+2]) << 16) | (uint32_t(data[offset+3]) << 24); } static inline uint16_t readLE16(const std::vector& data, size_t offset) { + if (offset + 2 > data.size()) return 0; return data[offset] | (uint16_t(data[offset+1]) << 8); } @@ -95,12 +99,14 @@ bool WardenMemory::parsePE(const std::vector& fileData) { if (rawDataSize == 0 || rawDataOffset == 0) continue; - // Clamp copy size to file and image bounds + // Clamp copy size to file and image bounds. + // Guard against underflow: if offset exceeds buffer size, skip the section + // entirely rather than wrapping to a huge uint32_t in the subtraction. + if (rawDataOffset >= fileData.size() || virtualAddr >= imageSize_) continue; uint32_t copySize = std::min(rawDataSize, virtualSize); - if (rawDataOffset + copySize > fileData.size()) - copySize = static_cast(fileData.size()) - rawDataOffset; - if (virtualAddr + copySize > imageSize_) - copySize = imageSize_ - virtualAddr; + uint32_t maxFromFile = static_cast(fileData.size()) - rawDataOffset; + uint32_t maxFromImage = imageSize_ - virtualAddr; + copySize = std::min({copySize, maxFromFile, maxFromImage}); std::memcpy(image_.data() + virtualAddr, fileData.data() + rawDataOffset, copySize);