6864911: ASN.1/DER input stream parser needs more work
authorweijun
Tue, 18 Aug 2009 12:10:12 +0800
changeset 4188 f67abce80f05
parent 3621 526f2b12a0db
child 4189 5fd64379cea5
6864911: ASN.1/DER input stream parser needs more work Reviewed-by: mullan, xuelei
jdk/src/share/classes/com/sun/jndi/ldap/Connection.java
jdk/src/share/classes/sun/applet/AppletClassLoader.java
jdk/src/share/classes/sun/dyn/anon/AnonymousClassLoader.java
jdk/src/share/classes/sun/misc/IOUtils.java
jdk/src/share/classes/sun/misc/Resource.java
jdk/src/share/classes/sun/reflect/misc/MethodUtil.java
jdk/src/share/classes/sun/security/provider/certpath/OCSPChecker.java
jdk/src/share/classes/sun/security/timestamp/HttpTimestamper.java
jdk/src/share/classes/sun/security/util/DerValue.java
jdk/test/sun/security/util/DerValue/BadValue.java
--- a/jdk/src/share/classes/com/sun/jndi/ldap/Connection.java	Thu Aug 13 15:12:32 2009 -0700
+++ b/jdk/src/share/classes/com/sun/jndi/ldap/Connection.java	Tue Aug 18 12:10:12 2009 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 1999-2009 Sun Microsystems, Inc.  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
@@ -32,12 +32,8 @@
 import java.io.OutputStream;
 import java.io.InputStream;
 import java.net.Socket;
-import java.util.Vector;
-import java.util.Hashtable;
 
 import javax.naming.CommunicationException;
-import javax.naming.AuthenticationException;
-import javax.naming.AuthenticationNotSupportedException;
 import javax.naming.ServiceUnavailableException;
 import javax.naming.NamingException;
 import javax.naming.InterruptedNamingException;
@@ -47,6 +43,8 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
+import java.util.Arrays;
+import sun.misc.IOUtils;
 //import javax.net.SocketFactory;
 
 /**
@@ -799,7 +797,6 @@
         byte inbuf[];   // Buffer for reading incoming bytes
         int inMsgId;    // Message id of incoming response
         int bytesread;  // Number of bytes in inbuf
-        int bytesleft;  // Number of bytes that need to read for completing resp
         int br;         // Temp; number of bytes read from stream
         int offset;     // Offset of where to store bytes in inbuf
         int seqlen;     // Length of ASN sequence
@@ -811,7 +808,7 @@
         try {
             while (true) {
                 try {
-                    inbuf = new byte[2048];
+                    inbuf = new byte[10];
 
                     offset = 0;
                     seqlen = 0;
@@ -871,19 +868,10 @@
                     }
 
                     // read in seqlen bytes
-                    bytesleft = seqlen;
-                    if ((offset + bytesleft) > inbuf.length) {
-                        byte nbuf[] = new byte[offset + bytesleft];
-                        System.arraycopy(inbuf, 0, nbuf, 0, offset);
-                        inbuf = nbuf;
-                    }
-                    while (bytesleft > 0) {
-                        bytesread = in.read(inbuf, offset, bytesleft);
-                        if (bytesread < 0)
-                            break; // EOF
-                        offset += bytesread;
-                        bytesleft -= bytesread;
-                    }
+                    byte[] left = IOUtils.readFully(in, seqlen, false);
+                    inbuf = Arrays.copyOf(inbuf, offset + left.length);
+                    System.arraycopy(left, 0, inbuf, offset, left.length);
+                    offset += left.length;
 /*
 if (dump > 0) {
 System.err.println("seqlen: " + seqlen);
--- a/jdk/src/share/classes/sun/applet/AppletClassLoader.java	Thu Aug 13 15:12:32 2009 -0700
+++ b/jdk/src/share/classes/sun/applet/AppletClassLoader.java	Tue Aug 18 12:10:12 2009 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright 1995-2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 1995-2009 Sun Microsystems, Inc.  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
@@ -51,6 +51,7 @@
 import java.security.PermissionCollection;
 import sun.awt.AppContext;
 import sun.awt.SunToolkit;
+import sun.misc.IOUtils;
 import sun.net.www.ParseUtil;
 import sun.security.util.SecurityConstants;
 
@@ -331,36 +332,7 @@
 
         byte[] b;
         try {
-            if (len != -1) {
-                // Read exactly len bytes from the input stream
-                b = new byte[len];
-                while (len > 0) {
-                    int n = in.read(b, b.length - len, len);
-                    if (n == -1) {
-                        throw new IOException("unexpected EOF");
-                    }
-                    len -= n;
-                }
-            } else {
-                // Read until end of stream is reached - use 8K buffer
-                // to speed up performance [stanleyh]
-                b = new byte[8192];
-                int total = 0;
-                while ((len = in.read(b, total, b.length - total)) != -1) {
-                    total += len;
-                    if (total >= b.length) {
-                        byte[] tmp = new byte[total * 2];
-                        System.arraycopy(b, 0, tmp, 0, total);
-                        b = tmp;
-                    }
-                }
-                // Trim array to correct size, if necessary
-                if (total != b.length) {
-                    byte[] tmp = new byte[total];
-                    System.arraycopy(b, 0, tmp, 0, total);
-                    b = tmp;
-                }
-            }
+            b = IOUtils.readFully(in, len, true);
         } finally {
             in.close();
         }
--- a/jdk/src/share/classes/sun/dyn/anon/AnonymousClassLoader.java	Thu Aug 13 15:12:32 2009 -0700
+++ b/jdk/src/share/classes/sun/dyn/anon/AnonymousClassLoader.java	Tue Aug 18 12:10:12 2009 +0800
@@ -26,9 +26,9 @@
 package sun.dyn.anon;
 
 import java.io.IOException;
-import java.io.InputStream;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import sun.misc.IOUtils;
 
 /**
  * Anonymous class loader.  Will load any valid classfile, producing
@@ -285,13 +285,6 @@
         if (contentLength < 0)
             throw new IOException("invalid content length "+contentLength);
 
-        byte[] classFile = new byte[contentLength];
-        InputStream tcs = connection.getInputStream();
-        for (int fill = 0, nr; fill < classFile.length; fill += nr) {
-            nr = tcs.read(classFile, fill, classFile.length - fill);
-            if (nr < 0)
-                throw new IOException("premature end of file");
-        }
-        return classFile;
+        return IOUtils.readFully(connection.getInputStream(), contentLength, true);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/misc/IOUtils.java	Tue Aug 18 12:10:12 2009 +0800
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * IOUtils: A collection of IO-related public static methods.
+ */
+
+package sun.misc;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+public class IOUtils {
+
+    /**
+     * Read up to <code>length</code> of bytes from <code>in</code>
+     * until EOF is detected.
+     * @param in input stream, must not be null
+     * @param length number of bytes to read, -1 or Integer.MAX_VALUE means
+     *        read as much as possible
+     * @param readAll if true, an EOFException will be thrown if not enough
+     *        bytes are read. Ignored when length is -1 or Integer.MAX_VALUE
+     * @return bytes read
+     * @throws IOException Any IO error or a premature EOF is detected
+     */
+    public static byte[] readFully(InputStream is, int length, boolean readAll)
+            throws IOException {
+        byte[] output = {};
+        if (length == -1) length = Integer.MAX_VALUE;
+        int pos = 0;
+        while (pos < length) {
+            int bytesToRead;
+            if (pos >= output.length) { // Only expand when there's no room
+                bytesToRead = Math.min(length - pos, output.length + 1024);
+                if (output.length < pos + bytesToRead) {
+                    output = Arrays.copyOf(output, pos + bytesToRead);
+                }
+            } else {
+                bytesToRead = output.length - pos;
+            }
+            int cc = is.read(output, pos, bytesToRead);
+            if (cc < 0) {
+                if (readAll && length != Integer.MAX_VALUE) {
+                    throw new EOFException("Detect premature EOF");
+                } else {
+                    if (output.length != pos) {
+                        output = Arrays.copyOf(output, pos);
+                    }
+                    break;
+                }
+            }
+            pos += cc;
+        }
+        return output;
+    }
+}
--- a/jdk/src/share/classes/sun/misc/Resource.java	Thu Aug 13 15:12:32 2009 -0700
+++ b/jdk/src/share/classes/sun/misc/Resource.java	Tue Aug 18 12:10:12 2009 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright 1998-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 1998-2009 Sun Microsystems, Inc.  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
@@ -25,14 +25,15 @@
 
 package sun.misc;
 
+import java.io.EOFException;
 import java.net.URL;
 import java.io.IOException;
 import java.io.InterruptedIOException;
 import java.io.InputStream;
 import java.security.CodeSigner;
 import java.util.jar.Manifest;
-import java.util.jar.Attributes;
 import java.nio.ByteBuffer;
+import java.util.Arrays;
 import sun.nio.ByteBuffered;
 
 /**
@@ -105,49 +106,37 @@
         }
 
         try {
-            if (len != -1) {
-                // Read exactly len bytes from the input stream
-                b = new byte[len];
-                while (len > 0) {
-                    int n = 0;
-                    try {
-                        n = in.read(b, b.length - len, len);
-                    } catch (InterruptedIOException iioe) {
-                        Thread.interrupted();
-                        isInterrupted = true;
+            b = new byte[0];
+            if (len == -1) len = Integer.MAX_VALUE;
+            int pos = 0;
+            while (pos < len) {
+                int bytesToRead;
+                if (pos >= b.length) { // Only expand when there's no room
+                    bytesToRead = Math.min(len - pos, b.length + 1024);
+                    if (b.length < pos + bytesToRead) {
+                        b = Arrays.copyOf(b, pos + bytesToRead);
                     }
-                    if (n == -1) {
-                        throw new IOException("unexpected EOF");
-                    }
-                    len -= n;
+                } else {
+                    bytesToRead = b.length - pos;
                 }
-            } else {
-                // Read until end of stream is reached
-                b = new byte[1024];
-                int total = 0;
-                for (;;) {
-                    len = 0;
-                    try {
-                        len = in.read(b, total, b.length - total);
-                        if (len == -1)
-                            break;
-                    } catch (InterruptedIOException iioe) {
-                        Thread.interrupted();
-                        isInterrupted = true;
-                    }
-                    total += len;
-                    if (total >= b.length) {
-                        byte[] tmp = new byte[total * 2];
-                        System.arraycopy(b, 0, tmp, 0, total);
-                        b = tmp;
+                int cc = 0;
+                try {
+                    cc = in.read(b, pos, bytesToRead);
+                } catch (InterruptedIOException iioe) {
+                    Thread.interrupted();
+                    isInterrupted = true;
+                }
+                if (cc < 0) {
+                    if (len != Integer.MAX_VALUE) {
+                        throw new EOFException("Detect premature EOF");
+                    } else {
+                        if (b.length != pos) {
+                            b = Arrays.copyOf(b, pos);
+                        }
+                        break;
                     }
                 }
-                // Trim array to correct size, if necessary
-                if (total != b.length) {
-                    byte[] tmp = new byte[total];
-                    System.arraycopy(b, 0, tmp, 0, total);
-                    b = tmp;
-                }
+                pos += cc;
             }
         } finally {
             try {
--- a/jdk/src/share/classes/sun/reflect/misc/MethodUtil.java	Thu Aug 13 15:12:32 2009 -0700
+++ b/jdk/src/share/classes/sun/reflect/misc/MethodUtil.java	Tue Aug 18 12:10:12 2009 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright 2005-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * Copyright 2005-2009 Sun Microsystems, Inc.  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
@@ -44,6 +44,7 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import sun.misc.IOUtils;
 import sun.net.www.ParseUtil;
 import sun.security.util.SecurityConstants;
 
@@ -373,34 +374,7 @@
 
         byte[] b;
         try {
-            if (len != -1) {
-                // Read exactly len bytes from the input stream
-                b = new byte[len];
-                while (len > 0) {
-                    int n = in.read(b, b.length - len, len);
-                    if (n == -1) {
-                        throw new IOException("unexpected EOF");
-                    }
-                    len -= n;
-                }
-            } else {
-                b = new byte[8192];
-                int total = 0;
-                while ((len = in.read(b, total, b.length - total)) != -1) {
-                    total += len;
-                    if (total >= b.length) {
-                        byte[] tmp = new byte[total * 2];
-                        System.arraycopy(b, 0, tmp, 0, total);
-                        b = tmp;
-                    }
-                }
-                // Trim array to correct size, if necessary
-                if (total != b.length) {
-                    byte[] tmp = new byte[total];
-                    System.arraycopy(b, 0, tmp, 0, total);
-                    b = tmp;
-                }
-            }
+            b = IOUtils.readFully(in, len, true);
         } finally {
             in.close();
         }
--- a/jdk/src/share/classes/sun/security/provider/certpath/OCSPChecker.java	Thu Aug 13 15:12:32 2009 -0700
+++ b/jdk/src/share/classes/sun/security/provider/certpath/OCSPChecker.java	Tue Aug 18 12:10:12 2009 +0800
@@ -37,6 +37,7 @@
 import java.net.*;
 import javax.security.auth.x500.X500Principal;
 
+import sun.misc.IOUtils;
 import sun.security.util.*;
 import sun.security.x509.*;
 
@@ -351,27 +352,8 @@
             }
             in = con.getInputStream();
 
-            byte[] response = null;
-            int total = 0;
             int contentLength = con.getContentLength();
-            if (contentLength != -1) {
-                response = new byte[contentLength];
-            } else {
-                response = new byte[2048];
-                contentLength = Integer.MAX_VALUE;
-            }
-
-            while (total < contentLength) {
-                int count = in.read(response, total, response.length - total);
-                if (count < 0)
-                    break;
-
-                total += count;
-                if (total >= response.length && total < contentLength) {
-                    response = Arrays.copyOf(response, total * 2);
-                }
-            }
-            response = Arrays.copyOf(response, total);
+            byte[] response = IOUtils.readFully(in, contentLength, false);
 
             OCSPResponse ocspResponse = new OCSPResponse(response, pkixParams,
                 responderCert);
--- a/jdk/src/share/classes/sun/security/timestamp/HttpTimestamper.java	Thu Aug 13 15:12:32 2009 -0700
+++ b/jdk/src/share/classes/sun/security/timestamp/HttpTimestamper.java	Tue Aug 18 12:10:12 2009 +0800
@@ -34,6 +34,7 @@
 import java.util.Set;
 import java.util.Arrays;
 
+import sun.misc.IOUtils;
 import sun.security.pkcs.*;
 
 /**
@@ -142,25 +143,7 @@
 
             int total = 0;
             int contentLength = connection.getContentLength();
-            if (contentLength != -1) {
-                replyBuffer = new byte[contentLength];
-            } else {
-                replyBuffer = new byte[2048];
-                contentLength = Integer.MAX_VALUE;
-            }
-
-            while (total < contentLength) {
-                int count = input.read(replyBuffer, total,
-                                        replyBuffer.length - total);
-                if (count < 0)
-                    break;
-
-                total += count;
-                if (total >= replyBuffer.length && total < contentLength) {
-                    replyBuffer = Arrays.copyOf(replyBuffer, total * 2);
-                }
-            }
-            replyBuffer = Arrays.copyOf(replyBuffer, total);
+            replyBuffer = IOUtils.readFully(input, contentLength, false);
 
             if (DEBUG) {
                 System.out.println("received timestamp response (length=" +
--- a/jdk/src/share/classes/sun/security/util/DerValue.java	Thu Aug 13 15:12:32 2009 -0700
+++ b/jdk/src/share/classes/sun/security/util/DerValue.java	Tue Aug 18 12:10:12 2009 +0800
@@ -28,6 +28,7 @@
 import java.io.*;
 import java.math.BigInteger;
 import java.util.Date;
+import sun.misc.IOUtils;
 
 /**
  * Represents a single DER-encoded value.  DER encoding rules are a subset
@@ -382,12 +383,8 @@
         if (fullyBuffered && in.available() != length)
             throw new IOException("extra data given to DerValue constructor");
 
-        byte[] bytes = new byte[length];
+        byte[] bytes = IOUtils.readFully(in, length, true);
 
-        // n.b. readFully not needed in normal fullyBuffered case
-        DataInputStream dis = new DataInputStream(in);
-
-        dis.readFully(bytes);
         buffer = new DerInputBuffer(bytes);
         return new DerInputStream(buffer);
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/util/DerValue/BadValue.java	Tue Aug 18 12:10:12 2009 +0800
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6864911
+ * @summary ASN.1/DER input stream parser needs more work
+ */
+
+import java.io.*;
+import sun.security.util.*;
+import sun.misc.IOUtils;
+
+public class BadValue {
+
+    public static void main(String[] args) throws Exception {
+
+        // Test IOUtils.readFully
+
+        // We have 4 bytes
+        InputStream in = new ByteArrayInputStream(new byte[10]);
+        byte[] bs = IOUtils.readFully(in, 4, true);
+        if (bs.length != 4 || in.available() != 6) {
+            throw new Exception("First read error");
+        }
+        // But only 6 left
+        bs = IOUtils.readFully(in, 10, false);
+        if (bs.length != 6 || in.available() != 0) {
+            throw new Exception("Second read error");
+        }
+        // MAX read as much as it can
+        in = new ByteArrayInputStream(new byte[10]);
+        bs = IOUtils.readFully(in, Integer.MAX_VALUE, true);
+        if (bs.length != 10 || in.available() != 0) {
+            throw new Exception("Second read error");
+        }
+        // MAX ignore readAll
+        in = new ByteArrayInputStream(new byte[10]);
+        bs = IOUtils.readFully(in, Integer.MAX_VALUE, false);
+        if (bs.length != 10 || in.available() != 0) {
+            throw new Exception("Second read error");
+        }
+        // 20>10, readAll means failure
+        in = new ByteArrayInputStream(new byte[10]);
+        try {
+            bs = IOUtils.readFully(in, 20, true);
+            throw new Exception("Third read error");
+        } catch (EOFException e) {
+            // OK
+        }
+        int bignum = 10 * 1024 * 1024;
+        bs = IOUtils.readFully(new SuperSlowStream(bignum), -1, true);
+        if (bs.length != bignum) {
+            throw new Exception("Fourth read error");
+        }
+
+        // Test DerValue
+        byte[] input = {0x04, (byte)0x84, 0x40, 0x00, 0x42, 0x46, 0x4b};
+        try {
+            new DerValue(new ByteArrayInputStream(input));
+        } catch (IOException ioe) {
+            // This is OK
+        }
+    }
+}
+
+/**
+ * An InputStream contains a given number of bytes, but only returns one byte
+ * per read.
+ */
+class SuperSlowStream extends InputStream {
+    private int p;
+    /**
+     * @param Initial capacity
+     */
+    public SuperSlowStream(int capacity) {
+        p = capacity;
+    }
+    @Override
+    public int read() throws IOException {
+        if (p > 0) {
+            p--;
+            return 0;
+        } else {
+            return -1;
+        }
+    }
+    @Override
+    public int read(byte b[], int off, int len) throws IOException {
+        if (len == 0) return 0;
+        if (p > 0) {
+            p--;
+            b[off] = 0;
+            return 1;
+        } else {
+            return -1;
+        }
+    }
+}