8191023: PngReader throws NegativeArraySizeException when keyword length exceeds chunk size
authorjdv
Tue, 30 Jan 2018 11:53:00 +0530
changeset 48728 fb62f481671e
parent 48727 8a99ef2b3558
child 48729 2d03ebb72df2
8191023: PngReader throws NegativeArraySizeException when keyword length exceeds chunk size Reviewed-by: serb, pnarayanan
src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java
test/jdk/javax/imageio/plugins/png/PngImproperChunkSizeTest.java
--- a/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java	Fri Jan 26 15:38:18 2018 -0800
+++ b/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageReader.java	Tue Jan 30 11:53:00 2018 +0530
@@ -428,12 +428,16 @@
 
     private void parse_iCCP_chunk(int chunkLength) throws IOException {
         String keyword = readNullTerminatedString("ISO-8859-1", 80);
+        int compressedProfileLength = chunkLength - keyword.length() - 2;
+        if (compressedProfileLength <= 0) {
+            throw new IIOException("iCCP chunk length is not proper");
+        }
         metadata.iCCP_profileName = keyword;
 
         metadata.iCCP_compressionMethod = stream.readUnsignedByte();
 
         byte[] compressedProfile =
-          new byte[chunkLength - keyword.length() - 2];
+          new byte[compressedProfileLength];
         stream.readFully(compressedProfile);
         metadata.iCCP_compressedProfile = compressedProfile;
 
@@ -463,7 +467,11 @@
 
         String text;
         pos = stream.getStreamPosition();
-        byte[] b = new byte[(int)(chunkStart + chunkLength - pos)];
+        int textLength = (int)(chunkStart + chunkLength - pos);
+        if (textLength <= 0) {
+            throw new IIOException("iTXt chunk length is not proper");
+        }
+        byte[] b = new byte[textLength];
         stream.readFully(b);
 
         if (compressionFlag == 1) { // Decompress the text
@@ -515,12 +523,16 @@
     private void parse_sPLT_chunk(int chunkLength)
         throws IOException, IIOException {
         metadata.sPLT_paletteName = readNullTerminatedString("ISO-8859-1", 80);
-        chunkLength -= metadata.sPLT_paletteName.length() + 1;
+        int remainingChunkLength = chunkLength -
+                (metadata.sPLT_paletteName.length() + 1);
+        if (remainingChunkLength <= 0) {
+            throw new IIOException("sPLT chunk length is not proper");
+        }
 
         int sampleDepth = stream.readUnsignedByte();
         metadata.sPLT_sampleDepth = sampleDepth;
 
-        int numEntries = chunkLength/(4*(sampleDepth/8) + 2);
+        int numEntries = remainingChunkLength/(4*(sampleDepth/8) + 2);
         metadata.sPLT_red = new int[numEntries];
         metadata.sPLT_green = new int[numEntries];
         metadata.sPLT_blue = new int[numEntries];
@@ -558,9 +570,13 @@
 
     private void parse_tEXt_chunk(int chunkLength) throws IOException {
         String keyword = readNullTerminatedString("ISO-8859-1", 80);
+        int textLength = chunkLength - keyword.length() - 1;
+        if (textLength <= 0) {
+            throw new IIOException("tEXt chunk length is not proper");
+        }
         metadata.tEXt_keyword.add(keyword);
 
-        byte[] b = new byte[chunkLength - keyword.length() - 1];
+        byte[] b = new byte[textLength];
         stream.readFully(b);
         metadata.tEXt_text.add(new String(b, "ISO-8859-1"));
 
@@ -596,7 +612,7 @@
             // Alpha table may have fewer entries than RGB palette
             int maxEntries = metadata.PLTE_red.length;
             int numEntries = chunkLength;
-            if (numEntries > maxEntries) {
+            if (numEntries > maxEntries && maxEntries > 0) {
                 processWarningOccurred(
 "tRNS chunk has more entries than prior PLTE chunk, ignoring extras.");
                 numEntries = maxEntries;
@@ -652,12 +668,16 @@
 
     private void parse_zTXt_chunk(int chunkLength) throws IOException {
         String keyword = readNullTerminatedString("ISO-8859-1", 80);
+        int textLength = chunkLength - keyword.length() - 2;
+        if (textLength <= 0) {
+            throw new IIOException("zTXt chunk length is not proper");
+        }
         metadata.zTXt_keyword.add(keyword);
 
         int method = stream.readUnsignedByte();
         metadata.zTXt_compressionMethod.add(method);
 
-        byte[] b = new byte[chunkLength - keyword.length() - 2];
+        byte[] b = new byte[textLength];
         stream.readFully(b);
         metadata.zTXt_text.add(new String(inflate(b), "ISO-8859-1"));
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/javax/imageio/plugins/png/PngImproperChunkSizeTest.java	Tue Jan 30 11:53:00 2018 +0530
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug     8191023
+ * @summary Test verifies that PNGImageReader doesn't throw any undocumented
+ *          Exception when we try to create byte array of negative size because
+ *          when keyword length is more than chunk size.
+ * @run     main PngImproperChunkSizeTest
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Base64;
+import javax.imageio.IIOException;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+
+public class PngImproperChunkSizeTest {
+
+    private static ImageReader reader;
+
+    private static String zTXTMalformedData = "iVBORw0KGgoAAAANSUhEUgAAAAEAAA" +
+            "ABCAAAAAA6fptVAAAABHpUWHRhYWFhYWFhYQAAAApJREFUGFdj+A8AAQEBAFpNb" +
+            "/EAAAAASUVORK5CYIIK";
+
+    private static String tEXTMalformedData = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB"
+            + "CAMAAAA6fptVAAAABHRFWHRhYWFhYWFhYQAAAApJREFUGFdj+A8AAQEBAFpNb"
+            + "/EAAAAASUVORK5CYIIK";
+
+    private static String iCCPMalformedData = "iVBORw0KGgoAAAANSUhEUgAAAAEAAA" +
+            "ABCAAAAAA6fptVAAAABGlDQ1BhYWFhYWFhYQAAAApJREFUGFdj+A8AAQEBAFpNb" +
+            "/EAAAAASUVORK5CYIIK";
+
+    private static ByteArrayInputStream initializeInputStream(String input) {
+        byte[] inputBytes = Base64.getDecoder().decode(input);
+        return new ByteArrayInputStream(inputBytes);
+    }
+
+    private static Boolean readzTXTData(InputStream input) throws IOException {
+        // Set input and mark ignoreMetadata = false
+        reader.setInput(ImageIO.createImageInputStream(input), true, false);
+        try {
+            reader.read(0);
+        } catch (IIOException e) {
+            Throwable cause = e.getCause();
+            if (cause == null ||
+                !cause.getMessage().
+                        equals("zTXt chunk length is not proper"))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static Boolean readtEXTData(InputStream input) throws IOException {
+        // Set input and mark ignoreMetadata = false
+        reader.setInput(ImageIO.createImageInputStream(input), true, false);
+        try {
+            reader.read(0);
+        } catch (IIOException e) {
+            Throwable cause = e.getCause();
+            if (cause == null ||
+                !cause.getMessage().
+                        equals("tEXt chunk length is not proper"))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static Boolean readiCCPData(InputStream input) throws IOException {
+        // Set input and mark ignoreMetadata = false
+        reader.setInput(ImageIO.createImageInputStream(input), true, false);
+        try {
+            reader.read(0);
+        } catch (IIOException e) {
+            Throwable cause = e.getCause();
+            if (cause == null ||
+                !cause.getMessage().
+                        equals("iCCP chunk length is not proper"))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static void main(String[] args) throws java.io.IOException {
+        reader = ImageIO.getImageReadersByFormatName("png").next();
+
+        InputStream in = initializeInputStream(zTXTMalformedData);
+        Boolean zTXTFailed = readzTXTData(in);
+
+        in = initializeInputStream(tEXTMalformedData);
+        Boolean tEXTFailed = readtEXTData(in);
+
+        in = initializeInputStream(iCCPMalformedData);
+        Boolean iCCPFailed = readiCCPData(in);
+
+        reader.dispose();
+
+        if (zTXTFailed || tEXTFailed || iCCPFailed) {
+            throw new RuntimeException("Test didn't throw the required" +
+                    " Exception");
+        }
+    }
+}
+