mirror of
https://github.com/opencv/opencv.git
synced 2026-07-30 15:53:03 +04:00
Merge pull request #29296 from arshsmith:fix-pam-grayalpha-overflow
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.
This commit is contained in:
@@ -175,28 +175,28 @@ rgb_convert (void *src, void *target, int width, int target_channels, int target
|
||||
*/
|
||||
|
||||
static void
|
||||
basic_conversion (void *src, const struct channel_layout *layout, int src_sampe_size,
|
||||
basic_conversion (void *src, const struct channel_layout *layout, int src_sample_size,
|
||||
int src_width, void *target, int target_channels, int target_depth, bool use_rgb)
|
||||
{
|
||||
switch (target_depth) {
|
||||
case CV_8U:
|
||||
{
|
||||
uchar *d = (uchar *)target, *s = (uchar *)src,
|
||||
*end = ((uchar *)src) + src_width;
|
||||
*end = ((uchar *)src) + src_width * src_sample_size;
|
||||
switch (target_channels) {
|
||||
case 1:
|
||||
for( ; s < end; d += 3, s += src_sampe_size )
|
||||
d[0] = d[1] = d[2] = s[layout->graychan];
|
||||
for( ; s < end; d += 1, s += src_sample_size )
|
||||
d[0] = s[layout->graychan];
|
||||
break;
|
||||
case 3:
|
||||
if (use_rgb)
|
||||
for( ; s < end; d += 3, s += src_sampe_size ) {
|
||||
for( ; s < end; d += 3, s += src_sample_size ) {
|
||||
d[0] = s[layout->rchan];
|
||||
d[1] = s[layout->gchan];
|
||||
d[2] = s[layout->bchan];
|
||||
}
|
||||
else
|
||||
for( ; s < end; d += 3, s += src_sampe_size ) {
|
||||
for( ; s < end; d += 3, s += src_sample_size ) {
|
||||
d[0] = s[layout->bchan];
|
||||
d[1] = s[layout->gchan];
|
||||
d[2] = s[layout->rchan];
|
||||
@@ -210,21 +210,21 @@ basic_conversion (void *src, const struct channel_layout *layout, int src_sampe_
|
||||
case CV_16U:
|
||||
{
|
||||
ushort *d = (ushort *)target, *s = (ushort *)src,
|
||||
*end = ((ushort *)src) + src_width;
|
||||
*end = ((ushort *)src) + src_width * src_sample_size;
|
||||
switch (target_channels) {
|
||||
case 1:
|
||||
for( ; s < end; d += 3, s += src_sampe_size )
|
||||
d[0] = d[1] = d[2] = s[layout->graychan];
|
||||
for( ; s < end; d += 1, s += src_sample_size )
|
||||
d[0] = s[layout->graychan];
|
||||
break;
|
||||
case 3:
|
||||
if (use_rgb)
|
||||
for( ; s < end; d += 3, s += src_sampe_size ) {
|
||||
for( ; s < end; d += 3, s += src_sample_size ) {
|
||||
d[0] = s[layout->rchan];
|
||||
d[1] = s[layout->gchan];
|
||||
d[2] = s[layout->bchan];
|
||||
}
|
||||
else
|
||||
for( ; s < end; d += 3, s += src_sampe_size ) {
|
||||
for( ; s < end; d += 3, s += src_sample_size ) {
|
||||
d[0] = s[layout->bchan];
|
||||
d[1] = s[layout->gchan];
|
||||
d[2] = s[layout->rchan];
|
||||
|
||||
Reference in New Issue
Block a user