Vulnerability: Out-of-bounds Write in OpenCV. In OpenCV 3.3.1 (corresponding with OpenCV-Python 3.3.1.11), a heap-based buffer overflow happens in cv::Jpeg2KDecoder::readComponent8u in modules/imgcodecs/src/grfmt_jpeg2000.cpp when parsing a crafted image file.
Vulnerability ID: GHSA-9g8h-pjm4-q92p
CWE: CWE-787
Severity score: 5.5
Project: https://github.com/opencv/opencv
Published: 2021-10-12
File: modules/imgcodecs/src/grfmt_jpeg2000.cpp
Commit message: imgcodecs: add more Jasper checks for supported and tested cases
Commit author: Alexander Alekhin

Vulnerable version (before fix):
 static JasperInitializer initialize_jasper;  Jpeg2KDecoder::Jpeg2KDecoder() {     m_signature = '\0' + String() + '\0' + String() + '\0' + String("\x0cjP  \r\n\x87\n");     m_stream = 0;     m_image = 0; } bool  Jpeg2KDecoder::readHeader()         jas_image_t* image = jas_image_decode( stream, -1, 0 );         m_image = image;         if( image ) {             m_width = jas_image_width( image );             m_height = jas_image_height( image );  bool  Jpeg2KDecoder::readHeader()             for( int i = 0; i < numcmpts; i++ )             {                 int depth_i = jas_image_cmptprec( image, i );                 depth = MAX(depth, depth_i);                 if( jas_image_cmpttype( image, i ) > 2 )                     continue;                 cntcmpts++;             }              if( cntcmpts )             {                 m_type = CV_MAKETYPE(depth <= 8 ? CV_8U : CV_16U, cntcmpts > 1 ? 3 : 1);                 result = true;             } bool  Jpeg2KDecoder::readHeader()     return result; }   bool  Jpeg2KDecoder::readData( Mat& img ) {     bool result = false;     int color = img.channels() > 1;     uchar* data = img.ptr(); bool  Jpeg2KDecoder::readData( Mat& img )                     result = true;                 }                 else                     fprintf(stderr, "JPEG 2000 LOADER ERROR: cannot convert colorspace\n");                 jas_cmprof_destroy( clrprof );             }             else                 fprintf(stderr, "JPEG 2000 LOADER ERROR: unable to create colorspace\n");         }         else             result = true; bool  Jpeg2KDecoder::readData( Mat& img )                                 result = readComponent16u( ((unsigned short *)data) + i, buffer, validateToInt(step / 2), cmptlut[i], maxval, offset, ncmpts );                             if( !result )                             {                                 i = ncmpts;                                 result = false;                             }                         }                         jas_matrix_destroy( buffer ); bool  Jpeg2KDecoder::readData( Mat& img )             }         }         else             fprintf(stderr, "JPEG2000 LOADER ERROR: colorspace conversion failed\n" );     }      close();  #ifndef _WIN32     if (!clr.empty())

Fixed version (after fix):
 static JasperInitializer initialize_jasper;  Jpeg2KDecoder::Jpeg2KDecoder() {     static const unsigned char signature_[12] = { 0, 0, 0, 0x0c, 'j', 'P', ' ', ' ', 13, 10, 0x87, 10};     m_signature = String((const char*)signature_, (const char*)signature_ + sizeof(signature_));     m_stream = 0;     m_image = 0; } bool  Jpeg2KDecoder::readHeader()         jas_image_t* image = jas_image_decode( stream, -1, 0 );         m_image = image;         if( image ) {             CV_Assert(0 == (jas_image_tlx(image)) && "not supported");             CV_Assert(0 == (jas_image_tly(image)) && "not supported");             m_width = jas_image_width( image );             m_height = jas_image_height( image );  bool  Jpeg2KDecoder::readHeader()             for( int i = 0; i < numcmpts; i++ )             {                 int depth_i = jas_image_cmptprec( image, i );                 CV_Assert(depth == 0 || depth == depth_i); // component data type mismatch                 depth = MAX(depth, depth_i);                 if( jas_image_cmpttype( image, i ) > 2 )                     continue;                 int sgnd = jas_image_cmptsgnd(image, i);                 int xstart = jas_image_cmpttlx(image, i);                 int xend = jas_image_cmptbrx(image, i);                 int xstep = jas_image_cmpthstep(image, i);                 int ystart = jas_image_cmpttly(image, i);                 int yend = jas_image_cmptbry(image, i);                 int ystep = jas_image_cmptvstep(image, i);                 CV_Assert(sgnd == 0 && "not supported");                 CV_Assert(xstart == 0 && "not supported");                 CV_Assert(ystart == 0 && "not supported");                 CV_Assert(xstep == 1 && "not supported");                 CV_Assert(ystep == 1 && "not supported");                 CV_Assert(xend == m_width);                 CV_Assert(yend == m_height);                 cntcmpts++;             }              if( cntcmpts )             {                 CV_Assert(depth == 8 || depth == 16);                 CV_Assert(cntcmpts == 1 || cntcmpts == 3);                 m_type = CV_MAKETYPE(depth <= 8 ? CV_8U : CV_16U, cntcmpts > 1 ? 3 : 1);                 result = true;             } bool  Jpeg2KDecoder::readHeader()     return result; }  static void Jpeg2KDecoder_close(Jpeg2KDecoder* ptr) {     ptr->close(); }  bool  Jpeg2KDecoder::readData( Mat& img ) {     Ptr<Jpeg2KDecoder> close_this(this, Jpeg2KDecoder_close);     bool result = false;     int color = img.channels() > 1;     uchar* data = img.ptr(); bool  Jpeg2KDecoder::readData( Mat& img )                     result = true;                 }                 else                 {                     jas_cmprof_destroy(clrprof);                     CV_Error(Error::StsError, "JPEG 2000 LOADER ERROR: cannot convert colorspace");                 }                 jas_cmprof_destroy( clrprof );             }             else             {                 CV_Error(Error::StsError, "JPEG 2000 LOADER ERROR: unable to create colorspace");             }         }         else             result = true; bool  Jpeg2KDecoder::readData( Mat& img )                                 result = readComponent16u( ((unsigned short *)data) + i, buffer, validateToInt(step / 2), cmptlut[i], maxval, offset, ncmpts );                             if( !result )                             {                                 jas_matrix_destroy( buffer );                                 CV_Error(Error::StsError, "JPEG2000 LOADER ERROR: failed to read component");                             }                         }                         jas_matrix_destroy( buffer ); bool  Jpeg2KDecoder::readData( Mat& img )             }         }         else         {             CV_Error(Error::StsError, "JPEG2000 LOADER ERROR: colorspace conversion failed");         }     }      CV_Assert(result == true);  #ifndef _WIN32     if (!clr.empty())

Patch:
patch: @@ -77,7 +77,8 @@ static JasperInitializer initialize_jasper;
 
 Jpeg2KDecoder::Jpeg2KDecoder()
 {
-    m_signature = '\0' + String() + '\0' + String() + '\0' + String("\x0cjP  \r\n\x87\n");
+    static const unsigned char signature_[12] = { 0, 0, 0, 0x0c, 'j', 'P', ' ', ' ', 13, 10, 0x87, 10};
+    m_signature = String((const char*)signature_, (const char*)signature_ + sizeof(signature_));
     m_stream = 0;
     m_image = 0;
 }
@@ -121,6 +122,8 @@ bool  Jpeg2KDecoder::readHeader()
         jas_image_t* image = jas_image_decode( stream, -1, 0 );
         m_image = image;
         if( image ) {
+            CV_Assert(0 == (jas_image_tlx(image)) && "not supported");
+            CV_Assert(0 == (jas_image_tly(image)) && "not supported");
             m_width = jas_image_width( image );
             m_height = jas_image_height( image );
 
@@ -130,14 +133,31 @@ bool  Jpeg2KDecoder::readHeader()
             for( int i = 0; i < numcmpts; i++ )
             {
                 int depth_i = jas_image_cmptprec( image, i );
+                CV_Assert(depth == 0 || depth == depth_i); // component data type mismatch
                 depth = MAX(depth, depth_i);
                 if( jas_image_cmpttype( image, i ) > 2 )
                     continue;
+                int sgnd = jas_image_cmptsgnd(image, i);
+                int xstart = jas_image_cmpttlx(image, i);
+                int xend = jas_image_cmptbrx(image, i);
+                int xstep = jas_image_cmpthstep(image, i);
+                int ystart = jas_image_cmpttly(image, i);
+                int yend = jas_image_cmptbry(image, i);
+                int ystep = jas_image_cmptvstep(image, i);
+                CV_Assert(sgnd == 0 && "not supported");
+                CV_Assert(xstart == 0 && "not supported");
+                CV_Assert(ystart == 0 && "not supported");
+                CV_Assert(xstep == 1 && "not supported");
+                CV_Assert(ystep == 1 && "not supported");
+                CV_Assert(xend == m_width);
+                CV_Assert(yend == m_height);
                 cntcmpts++;
             }
 
             if( cntcmpts )
             {
+                CV_Assert(depth == 8 || depth == 16);
+                CV_Assert(cntcmpts == 1 || cntcmpts == 3);
                 m_type = CV_MAKETYPE(depth <= 8 ? CV_8U : CV_16U, cntcmpts > 1 ? 3 : 1);
                 result = true;
             }
@@ -150,9 +170,14 @@ bool  Jpeg2KDecoder::readHeader()
     return result;
 }
 
+static void Jpeg2KDecoder_close(Jpeg2KDecoder* ptr)
+{
+    ptr->close();
+}
 
 bool  Jpeg2KDecoder::readData( Mat& img )
 {
+    Ptr<Jpeg2KDecoder> close_this(this, Jpeg2KDecoder_close);
     bool result = false;
     int color = img.channels() > 1;
     uchar* data = img.ptr();
@@ -204,11 +229,16 @@ bool  Jpeg2KDecoder::readData( Mat& img )
                     result = true;
                 }
                 else
-                    fprintf(stderr, "JPEG 2000 LOADER ERROR: cannot convert colorspace\n");
+                {
+                    jas_cmprof_destroy(clrprof);
+                    CV_Error(Error::StsError, "JPEG 2000 LOADER ERROR: cannot convert colorspace");
+                }
                 jas_cmprof_destroy( clrprof );
             }
             else
-                fprintf(stderr, "JPEG 2000 LOADER ERROR: unable to create colorspace\n");
+            {
+                CV_Error(Error::StsError, "JPEG 2000 LOADER ERROR: unable to create colorspace");
+            }
         }
         else
             result = true;
@@ -257,8 +287,8 @@ bool  Jpeg2KDecoder::readData( Mat& img )
                                 result = readComponent16u( ((unsigned short *)data) + i, buffer, validateToInt(step / 2), cmptlut[i], maxval, offset, ncmpts );
                             if( !result )
                             {
-                                i = ncmpts;
-                                result = false;
+                                jas_matrix_destroy( buffer );
+                                CV_Error(Error::StsError, "JPEG2000 LOADER ERROR: failed to read component");
                             }
                         }
                         jas_matrix_destroy( buffer );
@@ -267,10 +297,12 @@ bool  Jpeg2KDecoder::readData( Mat& img )
             }
         }
         else
-            fprintf(stderr, "JPEG2000 LOADER ERROR: colorspace conversion failed\n" );
+        {
+            CV_Error(Error::StsError, "JPEG2000 LOADER ERROR: colorspace conversion failed");
+        }
     }
 
-    close();
+    CV_Assert(result == true);
 
 #ifndef _WIN32
     if (!clr.empty())
path: modules/imgcodecs/src/grfmt_jpeg2000.cpp