6852563: ArrayOutOfBoundException when reading RLE8 compressed bitmap
authorpnarayanan
Tue, 14 Feb 2017 11:24:21 +0530
changeset 43835 bb3935761c12
parent 43834 ce43b8411c26
child 43836 dd08faade481
child 44126 be3651a1bc26
6852563: ArrayOutOfBoundException when reading RLE8 compressed bitmap Reviewed-by: prr, jdv
jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java	Tue Feb 14 10:00:01 2017 +0530
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/bmp/BMPImageReader.java	Tue Feb 14 11:24:21 2017 +0530
@@ -1439,51 +1439,72 @@
         decodeRLE8(imSize, padding, values, bdata);
     }
 
+    private boolean copyRLE8ScanlineToDst(int lineNo,
+                                          byte[] val,
+                                          byte[] bdata) {
+        // Return value
+        boolean isSuccess = false;
+
+        // Reusing the code to copy 1 row of pixels or scanline to required
+        // destination buffer.
+        if (lineNo >= sourceRegion.y &&
+            lineNo < sourceRegion.y + sourceRegion.height) {
+            if (noTransform) {
+                int pos = lineNo * width;
+                for(int i = 0; i < width; i++)
+                    bdata[pos++] = val[i];
+                processImageUpdate(bi, 0, lineNo,
+                                   destinationRegion.width, 1, 1, 1,
+                                   new int[]{0});
+                isSuccess = true;
+            } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
+                int lineStride =
+                    ((ComponentSampleModel)sampleModel).getScanlineStride();
+                int currentLine = (lineNo - sourceRegion.y) / scaleY +
+                    destinationRegion.y;
+                int pos = currentLine * lineStride;
+                pos += destinationRegion.x;
+                for (int i = sourceRegion.x;
+                     i < sourceRegion.x + sourceRegion.width;
+                     i += scaleX)
+                    bdata[pos++] = val[i];
+                processImageUpdate(bi, 0, currentLine,
+                                   destinationRegion.width, 1, 1, 1,
+                                   new int[]{0});
+                isSuccess = true;
+            }
+            // Ensure to reset the scanline buffer once the copy is complete.
+            for(int scIndex = 0; scIndex < width; scIndex++) {
+                val[scIndex] = 0;
+            }
+        }
+
+        return isSuccess;
+    }
+
     private void decodeRLE8(int imSize,
                             int padding,
                             byte[] values,
                             byte[] bdata) throws IOException {
 
-        byte val[] = new byte[width * height];
+        byte val[] = new byte[width];
         int count = 0, l = 0;
         int value;
         boolean flag = false;
         int lineNo = isBottomUp ? height - 1 : 0;
-        int lineStride =
-            ((ComponentSampleModel)sampleModel).getScanlineStride();
         int finished = 0;
 
-        while (count != imSize) {
+        // Ensure image source has sufficient data to decode
+        while ((count + 1) < imSize) {
             value = values[count++] & 0xff;
             if (value == 0) {
                 switch(values[count++] & 0xff) {
 
                 case 0:
                     // End-of-scanline marker
-                    if (lineNo >= sourceRegion.y &&
-                        lineNo < sourceRegion.y + sourceRegion.height) {
-                        if (noTransform) {
-                            int pos = lineNo * width;
-                            for(int i = 0; i < width; i++)
-                                bdata[pos++] = val[i];
-                            processImageUpdate(bi, 0, lineNo,
-                                               destinationRegion.width, 1, 1, 1,
-                                               new int[]{0});
-                            finished++;
-                        } else if ((lineNo - sourceRegion.y) % scaleY == 0) {
-                            int currentLine = (lineNo - sourceRegion.y) / scaleY +
-                                destinationRegion.y;
-                            int pos = currentLine * lineStride;
-                            pos += destinationRegion.x;
-                            for (int i = sourceRegion.x;
-                                 i < sourceRegion.x + sourceRegion.width;
-                                 i += scaleX)
-                                bdata[pos++] = val[i];
-                            processImageUpdate(bi, 0, currentLine,
-                                               destinationRegion.width, 1, 1, 1,
-                                               new int[]{0});
-                            finished++;
-                        }
+                    // Copy the decoded scanline to destination
+                    if (copyRLE8ScanlineToDst(lineNo, val, bdata)) {
+                        finished++;
                     }
                     processImageProgress(100.0F * finished / destinationRegion.height);
                     lineNo += isBottomUp ? -1 : 1;
@@ -1492,26 +1513,62 @@
                     if (abortRequested()) {
                         flag = true;
                     }
-
                     break;
 
                 case 1:
                     // End-of-RLE marker
                     flag = true;
+
+                    // Check if the last decoded scanline was copied to
+                    // destination bitmap
+                    if (l != 0) {
+                        // Copy the decoded scanline to destination
+                        if (copyRLE8ScanlineToDst(lineNo, val, bdata)) {
+                            finished++;
+                        }
+                        processImageProgress(100.0F * finished / destinationRegion.height);
+                        lineNo += isBottomUp ? -1 : 1;
+                        l = 0;
+                    }
                     break;
 
                 case 2:
                     // delta or vector marker
-                    int xoff = values[count++] & 0xff;
-                    int yoff = values[count] & 0xff;
-                    // Move to the position xoff, yoff down
-                    l += xoff + yoff*width;
+                    if ((count+1) < imSize) {
+                        int xoff = values[count++] & 0xff;
+                        int yoff = values[count++] & 0xff;
+
+                        // Check if the yOffset shifts the decoding to another
+                        // row. In such cases, the decoded pixels in scanline
+                        // buffer-val must be copied to the destination image.
+                        if (yoff != 0) {
+                            // Copy the decoded scanline to destination
+                            if (copyRLE8ScanlineToDst(lineNo, val, bdata)) {
+                                finished++;
+                            }
+                            processImageProgress(100.0F * finished
+                                                 / destinationRegion.height);
+                            lineNo += isBottomUp ? -yoff : yoff;
+                        }
+
+                        // Move to the position xoff, yoff down
+                        l += xoff + yoff*width;
+                        l %= width;
+                    }
                     break;
 
                 default:
                     int end = values[count-1] & 0xff;
-                    for (int i=0; i<end; i++) {
-                        val[l++] = (byte)(values[count++] & 0xff);
+                    byte readByte = 0;
+                    // Ensure to check if the source index-count, does not
+                    // exceed the source image size
+                    for (int i=0; (i < end) && (count < imSize); i++) {
+                        readByte = (byte)(values[count++] & 0xff);
+                        // Ensure to check if scanline index-l, does not
+                        // exceed the scanline buffer size (width of image)
+                        if (l < width) {
+                            val[l++] = readByte;
+                        }
                     }
 
                     // Whenever end pixels can fit into odd number of bytes,
@@ -1519,10 +1576,16 @@
                     if ((end & 1) == 1) {
                         count++;
                     }
+                    break;
                 }
             } else {
-                for (int i=0; i<value; i++) {
-                    val[l++] = (byte)(values[count] & 0xff);
+                // Encoded mode
+                // Ensure to check if the source index-count, does not
+                // exceed the source image size
+                if (count < imSize) {
+                    for (int i=0; (i < value) && (l < width); i++) {
+                        val[l++] = (byte)(values[count] & 0xff);
+                    }
                 }
 
                 count++;