From 62971de34ac45bc8926810122f65d1d001f91693 Mon Sep 17 00:00:00 2001 From: Dan Rosser Date: Sat, 15 Aug 2026 03:29:20 +1000 Subject: [PATCH] Fix heap buffer overflow in PSD RLE unpacker (CVE-2024-28565, CVE-2025-65803) psdParser::UnpackRLE() clamped the memcpy/memset write count against line_end, but then unconditionally advanced line/rle_line/srcSize by the raw, unclamped packet length: - line advanced past line_end when a packet was clamped, so a later iteration's `line_end - line` went negative and got reinterpreted as a huge size_t memcpy/memset count - heap buffer overflow WRITE. - srcSize (unsigned) underflowed to a huge value whenever a packet's claimed length exceeded the bytes actually left in the (attacker- controlled) rle_line source buffer, letting rle_line run past its real allocation on later iterations - heap buffer overflow READ. - The RLE-compressed packet branch also read one repeat-value byte from rle_line with no check that a byte was actually left in srcSize. Clamp packet length against both the remaining destination space and the remaining source bytes before using it to advance any pointer or counter, and stop the loop once either buffer is exhausted. CWE-787 / CWE-125 (Out-of-bounds Write / Read) Fixes: CVE-2024-28565 Fixes: CVE-2025-65803 --- Source/FreeImage/PSDParser.cpp | 40 ++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/Source/FreeImage/PSDParser.cpp b/Source/FreeImage/PSDParser.cpp index 81a395f..c3012d7 100644 --- a/Source/FreeImage/PSDParser.cpp +++ b/Source/FreeImage/PSDParser.cpp @@ -1308,7 +1308,16 @@ void psdParser::ReadImageLine(BYTE* dst, const BYTE* src, unsigned lineSize, uns } void psdParser::UnpackRLE(BYTE* line, const BYTE* rle_line, BYTE* line_end, unsigned srcSize) { - while (srcSize > 0) { + // NOTE: line/line_end bound the destination scanline; srcSize bounds how + // many bytes remain in the (attacker-controlled) rle_line source buffer. + // Both the copy length written to the destination AND the amount + // consumed from the source must be clamped *before* len is used to + // advance line/rle_line/srcSize - advancing by the raw, unclamped len + // let line overrun line_end (turning line_end - line negative, which + // becomes a huge size_t on the next iteration's memcpy/memset) and let + // srcSize (unsigned) underflow to a huge value when len > srcSize, + // letting rle_line run past the end of its real buffer. + while (srcSize > 0 && line < line_end) { int len = *rle_line++; srcSize--; @@ -1321,9 +1330,17 @@ void psdParser::UnpackRLE(BYTE* line, const BYTE* rle_line, BYTE* line_end, unsi // (len + 1) bytes of data are copied ++len; - // assert we don't write beyound eol - memcpy(line, rle_line, line + len > line_end ? line_end - line : len); - line += len; + // clamp to what's actually left in the source buffer + if ((unsigned)len > srcSize) { + len = (int)srcSize; + } + + // assert we don't write beyond eol + const auto remaining = line_end - line; + const int copyLen = (len > remaining) ? (int)remaining : len; + + memcpy(line, rle_line, copyLen); + line += copyLen; rle_line += len; srcSize -= len; } @@ -1335,10 +1352,19 @@ void psdParser::UnpackRLE(BYTE* line, const BYTE* rle_line, BYTE* line_end, unsi len ^= 0xFF; // same as (-len + 1) & 0xFF len += 2; // - // assert we don't write beyound eol - memset(line, *rle_line++, line + len > line_end ? line_end - line : len); - line += len; + if (srcSize < 1) { + // no repeat-value byte left in the source buffer + break; + } + const BYTE value = *rle_line++; srcSize--; + + // assert we don't write beyond eol + const auto remaining = line_end - line; + const int copyLen = (len > remaining) ? (int)remaining : len; + + memset(line, value, copyLen); + line += copyLen; } else if ( 128 == len ) { // Do nothing