6864911: ASN.1/DER input stream parser needs more work
Reviewed-by: mullan, xuelei
--- 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;
+ }
+ }
+}