GraphicsMagick: ReadSUNImage(): Enlarge RLE output buffer in ord...

GraphicsMagick Commits <[email protected]>
Newsgroups gmane.comp.video.graphicsmagick.cvs
Message-ID <mailman.38688.1672610248.1350.graphicsmagick-commit@lists.sourceforge.net>
changeset fe4699f9f06c in /hg/GraphicsMagick
details: http://hg.GraphicsMagick.org/hg/GraphicsMagick?cmd=changeset;node=fe4699f9f06c
summary: ReadSUNImage(): Enlarge RLE output buffer in order to avoid buffer overflow.

diffstat:

 ChangeLog                        |   11 +
 coders/sun.c                     |  773 ++++++++++++++++++++------------------
 coders/xpm.c                     |    4 +-
 magick/utility.c                 |    4 +
 tests/input_truecolor_1x266.miff |    5 +
 tests/rwfile.tap                 |    2 +-
 www/Changelog.html               |   12 +
 7 files changed, 435 insertions(+), 376 deletions(-)

diffs (truncated from 923 to 500 lines):

diff -r 758a0568bfc3 -r fe4699f9f06c ChangeLog
--- a/ChangeLog	Sun Jan 01 09:36:12 2023 -0600
+++ b/ChangeLog	Sun Jan 01 15:57:07 2023 -0600
@@ -1,3 +1,14 @@
+2023-01-01  Bob Friesenhahn  <[email protected]>
+
+	* magick/utility.c (GetMagickGeometry): Assure that width and
+	height are not scaled down to zero since it is an invalid value.
+
+	* coders/sun.c (ReadSUNImage): Enlarge RLE output buffer in order
+	to avoid buffer overflow.  Addresses oss-fuzz 54716
+	"graphicsmagick:coder_RAS_fuzzer: Heap-buffer-overflow in
+	ReadSUNImage", which is due to a new problem added since the
+	1.3.39 release.
+
 2023-01-01 Fojtik Jaroslav  <[email protected]>
 
 	* jp2/* Update lib jasper to 2.0.0.
diff -r 758a0568bfc3 -r fe4699f9f06c coders/sun.c
--- a/coders/sun.c	Sun Jan 01 09:36:12 2023 -0600
+++ b/coders/sun.c	Sun Jan 01 15:57:07 2023 -0600
@@ -303,6 +303,7 @@
     bytes_per_image,
     bytes_per_line,
     count,
+    pad,
     sun_data_length;
 
   SUNInfo
@@ -339,422 +340,448 @@
   (void) memset(&sun_info,0,sizeof(sun_info));
   sun_info.magic=ReadBlobMSBLong(image);
   do
-  {
-    /*
-      Verify SUN identifier.
-    */
-    if (sun_info.magic != 0x59a66a95)
-      ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
-    sun_info.width=ReadBlobMSBLong(image);
-    sun_info.height=ReadBlobMSBLong(image);
-    sun_info.depth=ReadBlobMSBLong(image);
-    sun_info.length=ReadBlobMSBLong(image);
-    sun_info.type=ReadBlobMSBLong(image);
-    sun_info.maptype=ReadBlobMSBLong(image);
-    sun_info.maplength=ReadBlobMSBLong(image);
-    if (logging)
-      LogSUNInfo(&sun_info);
-    if (EOFBlob(image))
-      ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
-    /*
-      Verify that width, height, depth, and length are not zero
-    */
-    if ((sun_info.width == 0) || (sun_info.height == 0) ||
-        (sun_info.depth == 0) || (sun_info.length == 0))
-      ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
+    {
+      /*
+        Verify SUN identifier.
+      */
+      if (sun_info.magic != 0x59a66a95)
+        ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
+      sun_info.width=ReadBlobMSBLong(image);
+      sun_info.height=ReadBlobMSBLong(image);
+      sun_info.depth=ReadBlobMSBLong(image);
+      sun_info.length=ReadBlobMSBLong(image);
+      sun_info.type=ReadBlobMSBLong(image);
+      sun_info.maptype=ReadBlobMSBLong(image);
+      sun_info.maplength=ReadBlobMSBLong(image);
+      if (logging)
+        LogSUNInfo(&sun_info);
+      if (EOFBlob(image))
+        ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
+      /*
+        Verify that width, height, depth, and length are not zero
+      */
+      if ((sun_info.width == 0) || (sun_info.height == 0) ||
+          (sun_info.depth == 0) || (sun_info.length == 0))
+        ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
+
+      /*
+        Verify that header values are in positive numeric range of a
+        32-bit 'int' even though we store them in an unsigned value.
+      */
+      if ((sun_info.magic | sun_info.width | sun_info.height | sun_info.depth |
+           sun_info.type | sun_info.maptype | sun_info.maplength) & (1U << 31))
+        ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
+      /*
+        Verify that we support the image sub-type
+      */
+      if ((sun_info.type != RT_STANDARD) &&
+          (sun_info.type != RT_ENCODED) &&
+          (sun_info.type != RT_FORMAT_RGB))
+        ThrowReaderException(CoderError,DataEncodingSchemeIsNotSupported,image);
+      /*
+        Verify that we support the colormap type
+      */
+      if ((sun_info.maptype != RMT_NONE) &&
+          (sun_info.maptype != RMT_EQUAL_RGB))
+        ThrowReaderException(CoderError,ColormapTypeNotSupported,image);
+      /*
+        Insist that map length is zero if there is no colormap.
+      */
+      if ((sun_info.maptype == RMT_NONE) && (sun_info.maplength != 0))
+        ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
+      /*
+        Insist on a supported depth
+      */
+      if ((sun_info.depth != 1) &&
+          (sun_info.depth != 8) &&
+          (sun_info.depth != 24) &&
+          (sun_info.depth != 32))
+        ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
+
+      image->columns=sun_info.width;
+      image->rows=sun_info.height;
+      if (((unsigned long) ((long) image->columns) != image->columns) ||
+          ((unsigned long) ((long) image->rows) != image->rows))
+        ThrowReaderException(CoderError,ImageColumnOrRowSizeIsNotSupported,image);
+      if (CheckImagePixelLimits(image, exception) != MagickPass)
+        ThrowReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
+      image->depth=sun_info.depth <= 8 ? 8 : QuantumDepth;
+      if (sun_info.depth < 24)
+        {
+          image->colors=sun_info.maplength;
+          if (sun_info.maptype == RMT_NONE)
+            image->colors=1U << sun_info.depth;
+          if (sun_info.maptype == RMT_EQUAL_RGB)
+            image->colors=sun_info.maplength/3U;
+        }
 
-    /*
-      Verify that header values are in positive numeric range of a
-      32-bit 'int' even though we store them in an unsigned value.
-    */
-    if ((sun_info.magic | sun_info.width | sun_info.height | sun_info.depth |
-         sun_info.type | sun_info.maptype | sun_info.maplength) & (1U << 31))
-      ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
-    /*
-      Verify that we support the image sub-type
-    */
-    if ((sun_info.type != RT_STANDARD) &&
-        (sun_info.type != RT_ENCODED) &&
-        (sun_info.type != RT_FORMAT_RGB))
-      ThrowReaderException(CoderError,DataEncodingSchemeIsNotSupported,image);
-    /*
-      Verify that we support the colormap type
-    */
-    if ((sun_info.maptype != RMT_NONE) &&
-        (sun_info.maptype != RMT_EQUAL_RGB))
-      ThrowReaderException(CoderError,ColormapTypeNotSupported,image);
-    /*
-      Insist that map length is zero if there is no colormap.
-    */
-    if ((sun_info.maptype == RMT_NONE) && (sun_info.maplength != 0))
-      ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
-    /*
-      Insist on a supported depth
-    */
-    if ((sun_info.depth != 1) &&
-        (sun_info.depth != 8) &&
-        (sun_info.depth != 24) &&
-        (sun_info.depth != 32))
-      ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
+      switch (sun_info.maptype)
+        {
+        case RMT_NONE:
+          {
+            if (sun_info.depth < 24)
+              {
+                /*
+                  Create linear color ramp.
+                */
+                if (!AllocateImageColormap(image,image->colors))
+                  ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
+                                       image);
+                if (logging)
+                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                                        "Allocated colormap with %u colors", image->colors);
+              }
+            break;
+          }
+        case RMT_EQUAL_RGB:
+          {
+            unsigned char
+              *sun_colormap;
 
-    image->columns=sun_info.width;
-    image->rows=sun_info.height;
-    if (((unsigned long) ((long) image->columns) != image->columns) ||
-        ((unsigned long) ((long) image->rows) != image->rows))
-      ThrowReaderException(CoderError,ImageColumnOrRowSizeIsNotSupported,image);
-    if (CheckImagePixelLimits(image, exception) != MagickPass)
-        ThrowReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
-    image->depth=sun_info.depth <= 8 ? 8 : QuantumDepth;
-    if (sun_info.depth < 24)
-      {
-        image->colors=sun_info.maplength;
-        if (sun_info.maptype == RMT_NONE)
-          image->colors=1U << sun_info.depth;
-        if (sun_info.maptype == RMT_EQUAL_RGB)
-          image->colors=sun_info.maplength/3U;
-      }
+            /*
+              Read SUN raster colormap.
+            */
+            if (!AllocateImageColormap(image,image->colors))
+              ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
+                                   image);
+            sun_colormap=MagickAllocateResourceLimitedMemory(unsigned char *,image->colors);
+            if (sun_colormap == (unsigned char *) NULL)
+              ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
+                                   image);
+            do
+              {
+                if (ReadBlob(image,image->colors,(char *) sun_colormap) !=
+                    image->colors)
+                  {
+                    status = MagickFail;
+                    break;
+                  }
+                for (i=0; i < (long) image->colors; i++)
+                  image->colormap[i].red=ScaleCharToQuantum(sun_colormap[i]);
+                if (ReadBlob(image,image->colors,(char *) sun_colormap) !=
+                    image->colors)
+                  {
+                    status = MagickFail;
+                    break;
+                  }
+                for (i=0; i < (long) image->colors; i++)
+                  image->colormap[i].green=ScaleCharToQuantum(sun_colormap[i]);
+                if (ReadBlob(image,image->colors,(char *) sun_colormap) !=
+                    image->colors)
+                  {
+                    status = MagickFail;
+                    break;
+                  }
+                for (i=0; i < (long) image->colors; i++)
+                  image->colormap[i].blue=ScaleCharToQuantum(sun_colormap[i]);
+                break;
+              } while (1);
+            MagickFreeResourceLimitedMemory(sun_colormap);
+            if (MagickFail == status)
+              ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
+            if (logging)
+              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                                    "Read colormap with %u colors", image->colors);
+            break;
+          }
+        case RMT_RAW:
+          {
+            unsigned char
+              *sun_colormap;
 
-    switch (sun_info.maptype)
-    {
-      case RMT_NONE:
-      {
-        if (sun_info.depth < 24)
-          {
             /*
-              Create linear color ramp.
+              Read SUN raster colormap.
             */
             if (!AllocateImageColormap(image,image->colors))
               ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
-                image);
+                                   image);
+            sun_colormap=MagickAllocateResourceLimitedMemory(unsigned char *,sun_info.maplength);
+            if (sun_colormap == (unsigned char *) NULL)
+              ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
+                                   image);
+            if (ReadBlob(image,sun_info.maplength,(char *) sun_colormap) !=
+                sun_info.maplength)
+              status = MagickFail;
+            MagickFreeResourceLimitedMemory(sun_colormap);
+            if (MagickFail == status)
+              ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
+            if (logging)
+              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                                    "Read colormap with %u colors (length %u)",
+                                    image->colors, sun_info.maplength);
+            break;
           }
-        break;
-      }
-      case RMT_EQUAL_RGB:
-      {
-        unsigned char
-          *sun_colormap;
+        default:
+          ThrowReaderException(CoderError,ColormapTypeNotSupported,image)
+            }
+      image->matte=(sun_info.depth == 32);
+      image->columns=sun_info.width;
+      image->rows=sun_info.height;
+      image->depth=8;
+      if (sun_info.depth < 8)
+        image->depth=sun_info.depth;
 
-        /*
-          Read SUN raster colormap.
-        */
-        if (!AllocateImageColormap(image,image->colors))
-          ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
-            image);
-        sun_colormap=MagickAllocateResourceLimitedMemory(unsigned char *,image->colors);
-        if (sun_colormap == (unsigned char *) NULL)
-          ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
-                               image);
-        do
-          {
-            if (ReadBlob(image,image->colors,(char *) sun_colormap) !=
-                image->colors)
-              {
-                status = MagickFail;
-                break;
-              }
-            for (i=0; i < (long) image->colors; i++)
-              image->colormap[i].red=ScaleCharToQuantum(sun_colormap[i]);
-            if (ReadBlob(image,image->colors,(char *) sun_colormap) !=
-                image->colors)
-              {
-                status = MagickFail;
-                break;
-              }
-            for (i=0; i < (long) image->colors; i++)
-              image->colormap[i].green=ScaleCharToQuantum(sun_colormap[i]);
-            if (ReadBlob(image,image->colors,(char *) sun_colormap) !=
-                image->colors)
-              {
-                status = MagickFail;
-                break;
-              }
-            for (i=0; i < (long) image->colors; i++)
-              image->colormap[i].blue=ScaleCharToQuantum(sun_colormap[i]);
-            break;
-          } while (1);
-        MagickFreeResourceLimitedMemory(sun_colormap);
-        if (MagickFail == status)
-          ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
-        break;
-      }
-      case RMT_RAW:
-      {
-        unsigned char
-          *sun_colormap;
+      if (image_info->ping)
+        {
+          CloseBlob(image);
+          return(image);
+        }
+
+      /*
+        Compute bytes per line and bytes per image for an unencoded
+        image.
 
-        /*
-          Read SUN raster colormap.
-        */
-        if (!AllocateImageColormap(image,image->colors))
-          ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
-                               image);
-        sun_colormap=MagickAllocateResourceLimitedMemory(unsigned char *,sun_info.maplength);
-        if (sun_colormap == (unsigned char *) NULL)
-          ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,
-            image);
-        if (ReadBlob(image,sun_info.maplength,(char *) sun_colormap) !=
-            sun_info.maplength)
-          status = MagickFail;
-        MagickFreeResourceLimitedMemory(sun_colormap);
-        if (MagickFail == status)
-          ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
-        break;
-      }
-      default:
-        ThrowReaderException(CoderError,ColormapTypeNotSupported,image)
-    }
-    image->matte=(sun_info.depth == 32);
-    image->columns=sun_info.width;
-    image->rows=sun_info.height;
-    image->depth=8;
-    if (sun_info.depth < 8)
-      image->depth=sun_info.depth;
+        "The width of a scan line is always 16-bits, padded when necessary."
+      */
+      bytes_per_line=MagickArraySize(sun_info.width,sun_info.depth);
+      if (bytes_per_line == 0)
+        ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
+
+      pad = sun_info.depth == 1 ? 15U : 7U;  /* Pad */
+      bytes_per_line += pad;
 
-    if (image_info->ping)
-      {
-        CloseBlob(image);
-        return(image);
-      }
+      if (bytes_per_line != ((size_t) sun_info.width*sun_info.depth+pad))
+        ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
 
-    /*
-      Compute bytes per line and bytes per image for an unencoded
-      image.
+      bytes_per_line /= 8U;
+      if (bytes_per_line == 0)
+        ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
+
+      bytes_per_image=MagickArraySize(sun_info.height,bytes_per_line);
+      if (bytes_per_image == 0)
+        ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
 
-      "The width of a scan line is always 16-bits, padded when necessary."
-    */
-    bytes_per_line=MagickArraySize(sun_info.width,sun_info.depth);
-    if (bytes_per_line == 0)
-      ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
-
-    bytes_per_line += sun_info.depth == 1 ? 15U : 7U; /* Pad */
-    bytes_per_line /= 8U;
-    if (bytes_per_line == 0)
-      ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
-
-    bytes_per_image=MagickArraySize(sun_info.height,bytes_per_line);
-    if (bytes_per_image == 0)
-      ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
-
-    if (sun_info.type == RT_ENCODED)
-      sun_data_length=(size_t) sun_info.length;
-    else
-      sun_data_length=bytes_per_image;
+      if (sun_info.type == RT_ENCODED)
+        sun_data_length=(size_t) sun_info.length;
+      else
+        sun_data_length=bytes_per_image;
 
-    /*
-      Verify that data length claimed by header is supported by file size
-    */
-    if (sun_info.type == RT_ENCODED)
-      {
-        if (sun_data_length < bytes_per_image/255U)
-          ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
-      }
-    if (BlobIsSeekable(image))
-      {
-        const magick_off_t file_size = GetBlobSize(image);
-        const magick_off_t current_offset = TellBlob(image);
-        if ((file_size > 0) &&
-            (current_offset > 0) &&
-            (file_size >= current_offset))
+      /*
+        Verify that data length claimed by header is supported by file size
+      */
+      if (sun_info.type == RT_ENCODED)
+        {
+          if (sun_data_length < bytes_per_image/255U)
+            ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
+        }
+      if (BlobIsSeekable(image))
         {
-          const magick_off_t remaining = file_size-current_offset;
+          const magick_off_t file_size = GetBlobSize(image);
+          const magick_off_t current_offset = TellBlob(image);
+          if ((file_size > 0) &&
+              (current_offset > 0) &&
+              (file_size >= current_offset))
+            {
+              const magick_off_t remaining = file_size-current_offset;
 
-          if ((remaining == 0) || (remaining < (magick_off_t) sun_info.length))
-            {
-              ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
+              if ((remaining == 0) || (remaining < (magick_off_t) sun_info.length))
+                {
+                  ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
+                }
             }
         }
-      }
 
-    /*
-      Read raster data into allocated buffer.
-    */
-    sun_data=MagickAllocateResourceLimitedMemory(unsigned char *,sun_info.length);
-    if (sun_data == (unsigned char *) NULL)
-      ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
-    if ((count=ReadBlob(image,sun_info.length,(char *) sun_data))
-        != sun_info.length)
-      {
-        MagickFreeResourceLimitedMemory(sun_data);
-        ThrowReaderException(CorruptImageError,UnableToReadImageData,image);
-      }
-    sun_pixels=sun_data;
-    if (sun_info.type == RT_ENCODED)
-      {
-        /*
-          Read run-length encoded raster pixels (padded to 16-bit boundary).
-        */
-        sun_pixels=MagickAllocateResourceLimitedMemory(unsigned char *,bytes_per_image);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.