CVE-2021-46389: Integer Overflow or Wraparound (CWE-190)
value: IIPImage High Resolution Streaming Image Server prior to commit 882925b295a80ec992063deffc2a3b0d803c3195 is affected by an integer overflow in iipsrv.fcgi through malformed HTTP query parameters.
CWE description: The product performs a calculation that can
         produce an integer overflow or wraparound when the logic
         assumes that the resulting value will always be larger than
         the original value. This occurs when an integer value is
         incremented to a value that is too large to store in the
         associated representation. When this occurs, the value may
         become a very small or negative number.
CVSS v3 base score: 7.5
Published: 2022-02-07T14:15Z
Repository: https://github.com/ruven/iipsrv
Language: C++
Files changed: ChangeLog, src/KakaduImage.cc, src/OpenJPEGImage.cc, src/RawTile.h, src/TileManager.cc, src/Transforms.cc
Commit date: 2022-01-16 14:34:07 +0100
Commit message: - Modified TileManager.cc to verify that malloc() has correctly allocated memory.
- Updated numerical types to std::size_t in RawTile.h, TileManager.cc, KakaduImage.cc, OpenJPEG.cc and Transforms.cc when allocating memory via new to avoid integer overflow - fixes remaining problems identified in https://github.com/ruven/iipsrv/issues/223.

Diff: diff --git a/ChangeLog b/ChangeLog
index 6725c69..06c0081 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
+16/01/2022:
+	- Modified TileManager.cc to verify that malloc() has correctly allocated memory.
+	- Updated numerical types to std::size_t in RawTile.h, TileManager.cc, KakaduImage.cc, OpenJPEG.cc and Transforms.cc
+	  when allocating memory via new to avoid integer overflow - fixes remaining problems identified in
+	  https://github.com/ruven/iipsrv/issues/223.
+
+
 15/01/2022:
 	- Added verification that image has been set in SPECTRA.cc and check on the validity of the requested tile
 	  resolution in JTL.cc. Fixes a couple of the crash conditions reported in https://github.com/ruven/iipsrv/issues/223
 
 
diff --git a/src/KakaduImage.cc b/src/KakaduImage.cc
index 0016a78..8893c64 100644
--- a/src/KakaduImage.cc
+++ b/src/KakaduImage.cc
@@ -445,15 +445,16 @@ RawTile KakaduImage::getRegion( int seq, int ang, unsigned int res, int layers,
   timer.start();
 #endif
 
   RawTile rawtile( 0, res, seq, ang, w, h, channels, obpc );
 
-  if( obpc == 16 ) rawtile.data = new unsigned short[w*h*channels];
-  else if( obpc == 8 ) rawtile.data = new unsigned char[w*h*channels];
+  size_t np = (size_t) w * (size_t) h * (size_t) channels;
+  if( obpc == 16 ) rawtile.data = new unsigned short[np];
+  else if( obpc == 8 ) rawtile.data = new unsigned char[np];
   else throw file_error( "Kakadu :: Unsupported number of bits" );
 
-  rawtile.dataLength = w*h*channels*(obpc/8);
+  rawtile.dataLength = np*(obpc/8);
   rawtile.filename = getImagePath();
   rawtile.timestamp = timestamp;
 
   process( res, layers, x, y, w, h, rawtile.data );
 
diff --git a/src/OpenJPEGImage.cc b/src/OpenJPEGImage.cc
index 59da6dc..1c31580 100644
--- a/src/OpenJPEGImage.cc
+++ b/src/OpenJPEGImage.cc
@@ -374,15 +374,16 @@ RawTile OpenJPEGImage::getRegion( int ha, int va, unsigned int res, int layers,
   timer.start();
 #endif
 
   RawTile rawtile( 0, res, ha, va, w, h, channels, obpc );
 
-  if( obpc == 16 ) rawtile.data = new unsigned short[w * h * channels];
-  else if( obpc == 8 ) rawtile.data = new unsigned char[w * h * channels];
+  size_t np = (size_t) w * (size_t) h * (size_t) channels;
+  if( obpc == 16 ) rawtile.data = new unsigned short[np];
+  else if( obpc == 8 ) rawtile.data = new unsigned char[np];
   else throw file_error( "OpenJPEG :: Unsupported number of bits" );
 
-  rawtile.dataLength = w*h*channels*(obpc/8);
+  rawtile.dataLength = np*(obpc/8);
   rawtile.filename = getImagePath();
   rawtile.timestamp = timestamp;
 
   process( res, layers, x, y, w, h, rawtile.data );
 
diff --git a/src/RawTile.h b/src/RawTile.h
index 9406224..7aedb0f 100644
--- a/src/RawTile.h
+++ b/src/RawTile.h
@@ -77,11 +77,11 @@ class RawTile{
   /// or whether it is simply a pointer
   /** This is used in the destructor to make sure we deallocate correctly */
   int memoryManaged;
 
   /// The size of the data pointed to by data
-  unsigned int dataLength;
+  size_t dataLength;
 
   /// The width in pixels of this tile
   unsigned int width;
 
   /// The height in pixels of this tile
diff --git a/src/TileManager.cc b/src/TileManager.cc
index 4e2322e..a6ccdb3 100644
--- a/src/TileManager.cc
+++ b/src/TileManager.cc
@@ -2,11 +2,11 @@
 // Member functions for TileManager.h
 
 
 /*  IIP Server: Tile Cache Handler
 
-    Copyright (C) 2005-2021 Ruven Pillay.
+    Copyright (C) 2005-2022 Ruven Pillay.
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 3 of the License, or
     (at your option) any later version.
@@ -128,12 +128,18 @@ void TileManager::crop( RawTile *ttt ){
 	     << endl;
   }
 
   // Create a new buffer, fill it with the old data, then copy
   // back the cropped part into the RawTile buffer
-  int len = tw * th * ttt->channels * (ttt->bpc/8);
+  unsigned int len = tw * th * ttt->channels * (ttt->bpc/8);
   unsigned char* buffer = (unsigned char*) malloc( len );
+
+  // Check whether we have successfully allocated memory via malloc
+  if( buffer == NULL ){
+    std::bad_alloc e;
+    throw e;
+  }
   unsigned char* src_ptr = (unsigned char*) memcpy( buffer, ttt->data, len );
   unsigned char* dst_ptr = (unsigned char*) ttt->data;
 
   // Copy one scanline at a time
   len =  ttt->width * ttt->channels * (ttt->bpc/8);
@@ -363,25 +369,26 @@ RawTile TileManager::getRegion( unsigned int res, int seq, int ang, int layers,
   // Assume 1 bit data has been unpacked to 8 bits per channel
   if( bpc == 1 ) bpc = 8;
 
   // Create an empty tile with the correct dimensions
   RawTile region( 0, res, seq, ang, width, height, channels, bpc );
-  region.dataLength = width * height * channels * (bpc/8);
+  size_t np = (size_t) width * (size_t) height * (size_t) channels;
+  region.dataLength = np * (bpc/8);
   region.sampleType = sampleType;
 
   // Allocate memory for the region
-  if( bpc == 8 ) region.data = new unsigned char[width*height*channels];
-  else if( bpc == 16 ) region.data = new unsigned short[width*height*channels];
-  else if( bpc == 32 && sampleType == FIXEDPOINT ) region.data = new int[width*height*channels];
-  else if( bpc == 32 && sampleType == FLOATINGPOINT ) region.data = new float[width*height*channels];
+  if( bpc == 8 ) region.data = new unsigned char[np];
+  else if( bpc == 16 ) region.data = new unsigned short[np];
+  else if( bpc == 32 && sampleType == FIXEDPOINT ) region.data = new int[np];
+  else if( bpc == 32 && sampleType == FLOATINGPOINT ) region.data = new float[np];
 
   unsigned int current_height = 0;
 
   // Decode the image strip by strip
   for( unsigned int i=starty; i<endy; i++ ){
 
-    unsigned int buffer_index = 0;
+    unsigned long buffer_index = 0;
 
     // Keep track of the current pixel boundary horizontally. ie. only up
     //  to the beginning of the current tile boundary.
     unsigned int current_width = 0;
 
diff --git a/src/Transforms.cc b/src/Transforms.cc
index 430a046..8f9cfa5 100644
--- a/src/Transforms.cc
+++ b/src/Transforms.cc
@@ -530,11 +530,11 @@ void Transform::interpolate_nearestneighbour( RawTile& in, unsigned int resample
   if( new_buffer ) delete[] (unsigned char*) input;
 
   // Correctly set our Rawtile info
   in.width = resampled_width;
   in.height = resampled_height;
-  in.dataLength = resampled_width * resampled_height * channels * (in.bpc/8);
+  in.dataLength = (size_t)resampled_width * (size_t)resampled_height * (size_t)channels * (size_t)(in.bpc/8);
   in.data = output;
 }
 
 
 
@@ -616,11 +616,11 @@ void Transform::interpolate_bilinear( RawTile& in, unsigned int resampled_width,
   delete[] (unsigned char*) input;
 
   // Correctly set our Rawtile info
   in.width = resampled_width;
   in.height = resampled_height;
-  in.dataLength = resampled_width * resampled_height * channels * (in.bpc/8);
+  in.dataLength = (size_t)resampled_width * (size_t)resampled_height * (size_t)channels * (size_t)(in.bpc/8);
   in.data = output;
 }
 
 
 

Vulnerable code:   if( obpc == 16 ) rawtile.data = new unsigned short[w*h*channels];
  else if( obpc == 8 ) rawtile.data = new unsigned char[w*h*channels];
  rawtile.dataLength = w*h*channels*(obpc/8);
  if( obpc == 16 ) rawtile.data = new unsigned short[w * h * channels];
  else if( obpc == 8 ) rawtile.data = new unsigned char[w * h * channels];
  rawtile.dataLength = w*h*channels*(obpc/8);
  unsigned int dataLength;
    Copyright (C) 2005-2021 Ruven Pillay.
  int len = tw * th * ttt->channels * (ttt->bpc/8);
  region.dataLength = width * height * channels * (bpc/8);
  if( bpc == 8 ) region.data = new unsigned char[width*height*channels];
  else if( bpc == 16 ) region.data = new unsigned short[width*height*channels];
  else if( bpc == 32 && sampleType == FIXEDPOINT ) region.data = new int[width*height*channels];
  else if( bpc == 32 && sampleType == FLOATINGPOINT ) region.data = new float[width*height*channels];
    unsigned int buffer_index = 0;
  in.dataLength = resampled_width * resampled_height * channels * (in.bpc/8);
  in.dataLength = resampled_width * resampled_height * channels * (in.bpc/8);
Fixed code: 16/01/2022:
	- Modified TileManager.cc to verify that malloc() has correctly allocated memory.
	- Updated numerical types to std::size_t in RawTile.h, TileManager.cc, KakaduImage.cc, OpenJPEG.cc and Transforms.cc
	  when allocating memory via new to avoid integer overflow - fixes remaining problems identified in
	  https://github.com/ruven/iipsrv/issues/223.


  size_t np = (size_t) w * (size_t) h * (size_t) channels;
  if( obpc == 16 ) rawtile.data = new unsigned short[np];
  else if( obpc == 8 ) rawtile.data = new unsigned char[np];
  rawtile.dataLength = np*(obpc/8);
  size_t np = (size_t) w * (size_t) h * (size_t) channels;
  if( obpc == 16 ) rawtile.data = new unsigned short[np];
  else if( obpc == 8 ) rawtile.data = new unsigned char[np];
  rawtile.dataLength = np*(obpc/8);
  size_t dataLength;
    Copyright (C) 2005-2022 Ruven Pillay.
  unsigned int len = tw * th * ttt->channels * (ttt->bpc/8);

  // Check whether we have successfully allocated memory via malloc
  if( buffer == NULL ){
    std::bad_alloc e;
    throw e;
  }
  size_t np = (size_t) width * (size_t) height * (size_t) channels;
  region.dataLength = np * (bpc/8);
  if( bpc == 8 ) region.data = new unsigned char[np];
  else if( bpc == 16 ) region.data = new unsigned short[np];
  else if( bpc == 32 && sampleType == FIXEDPOINT ) region.data = new int[np];
  else if( bpc == 32 && sampleType == FLOATINGPOINT ) region.data = new float[np];
    unsigned long buffer_index = 0;
  in.dataLength = (size_t)resampled_width * (size_t)resampled_height * (size_t)channels * (size_t)(in.bpc/8);
  in.dataLength = (size_t)resampled_width * (size_t)resampled_height * (size_t)channels * (size_t)(in.bpc/8);