mirror of
https://github.com/opencv/opencv.git
synced 2026-07-21 19:33:03 +04:00
6df9732c81
imgcodecs(pam): fix out-of-bounds write when reading 2-channel PAM #29296 While poking at the PAM decoder I noticed basic_conversion() writes past the end of the destination buffer when a GRAYSCALE_ALPHA (2 channel) image is read with IMREAD_GRAYSCALE. The 1-channel branch was written as if the destination had 3 channels: for( ; s < end; d += 3, s += src_sampe_size ) d[0] = d[1] = d[2] = s[layout->graychan]; So for every source pixel it writes 3 bytes and advances d by 3, even though the output row only has m_width bytes (1 channel). With m_channels == 2 that ends up writing ~1.5 * m_width bytes per row, which runs off the row and, on the last row, off the end of the Mat. m_width is taken straight from the file header (up to 2^20), so the overflow size and contents are attacker controlled. It's reachable with a plain imread(file, IMREAD_GRAYSCALE) on a crafted file. While looking at it I also realised the loop bound was off for any multi channel source: end was set to src + src_width, but the source has src_width * channels samples, so it only ever processed m_width/channels pixels instead of all of them. That's why GRAYSCALE_ALPHA / RGB_ALPHA come out only partially filled. Fix both at once: - end now covers the whole row (src_width * src_sampe_size) - the 1-channel branch writes one byte and advances d by 1 For the common grayscale->color case (channels == 1) the new end is identical to the old one (m_width * 1 == m_width), so that path is byte for byte the same and the existing PAM read_write test is unaffected. The only outputs that change are the GRAYSCALE_ALPHA/RGB_ALPHA conversions, which were already broken. Added a regression test (Imgcodecs_Pam.decode_graya_as_gray) that builds a small 2-channel PAM with an odd width, decodes it as grayscale and checks the result matches the gray channel. It overflows/crashes on the old code and passes now.