diff --git a/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java index 2d09c795743d1d6b9bd22c39a33d5a77099d0d42..0024f2fb5bfee18f5988de6dcc7a5273945ec5e4 100644 --- a/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java +++ b/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2010, 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 @@ -77,6 +77,8 @@ public class ClassReader implements Completer { protected static final Context.Key classReaderKey = new Context.Key(); + public static final int INITIAL_BUFFER_SIZE = 0x0fff0; + Annotate annotate; /** Switch: verbose output. @@ -185,7 +187,7 @@ public class ClassReader implements Completer { /** The buffer containing the currently read class file. */ - byte[] buf = new byte[0x0fff0]; + byte[] buf = new byte[INITIAL_BUFFER_SIZE]; /** The current input pointer. */ @@ -2419,8 +2421,14 @@ public class ClassReader implements Completer { } } } + /* + * ensureCapacity will increase the buffer as needed, taking note that + * the new buffer will always be greater than the needed and never + * exactly equal to the needed size or bp. If equal then the read (above) + * will infinitely loop as buf.length - bp == 0. + */ private static byte[] ensureCapacity(byte[] buf, int needed) { - if (buf.length < needed) { + if (buf.length <= needed) { byte[] old = buf; buf = new byte[Integer.highestOneBit(needed) << 1]; System.arraycopy(old, 0, buf, 0, old.length); diff --git a/test/tools/javac/6567415/T6567415.java b/test/tools/javac/6567415/T6567415.java new file mode 100644 index 0000000000000000000000000000000000000000..683197e6e84f0e62c901bd8481c2c95c04509811 --- /dev/null +++ b/test/tools/javac/6567415/T6567415.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2010, 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 6567415 + * @summary Test to ensure javac does not go into an infinite loop, while + * reading a classfile of a specific length. + * @compile -XDignore.symbol.file T6567415.java + * @run main T6567415 + * @author ksrini + */ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; + +/* + * this test compiles Bar.java into a classfile and enlarges the file to the + * magic file length, then use this mutated file on the classpath to compile + * Foo.java which references Bar.java and Ka-boom. QED. + */ +public class T6567415 { + final static String TEST_FILE_NAME = "Bar"; + final static String TEST_JAVA = TEST_FILE_NAME + ".java"; + final static String TEST_CLASS = TEST_FILE_NAME + ".class"; + + final static String TEST2_FILE_NAME = "Foo"; + final static String TEST2_JAVA = TEST2_FILE_NAME + ".java"; + + /* + * the following is the initial buffer length set in ClassReader.java + * thus this value needs to change if ClassReader buf length changes. + */ + + final static int BAD_FILE_LENGTH = + com.sun.tools.javac.jvm.ClassReader.INITIAL_BUFFER_SIZE; + + static void createClassFile() throws IOException { + FileOutputStream fos = null; + try { + fos = new FileOutputStream(TEST_JAVA); + PrintStream ps = new PrintStream(fos); + ps.println("public class " + TEST_FILE_NAME + " {}"); + } finally { + fos.close(); + } + String cmds[] = {TEST_JAVA}; + com.sun.tools.javac.Main.compile(cmds); + } + + static void enlargeClassFile() throws IOException { + File f = new File(TEST_CLASS); + if (!f.exists()) { + System.out.println("file not found: " + TEST_CLASS); + System.exit(1); + } + File tfile = new File(f.getAbsolutePath() + ".tmp"); + f.renameTo(tfile); + + RandomAccessFile raf = null; + FileChannel wfc = null; + + FileInputStream fis = null; + FileChannel rfc = null; + + try { + raf = new RandomAccessFile(f, "rw"); + wfc = raf.getChannel(); + + fis = new FileInputStream(tfile); + rfc = fis.getChannel(); + + ByteBuffer bb = MappedByteBuffer.allocate(BAD_FILE_LENGTH); + rfc.read(bb); + bb.rewind(); + wfc.write(bb); + wfc.truncate(BAD_FILE_LENGTH); + } finally { + wfc.close(); + raf.close(); + rfc.close(); + fis.close(); + } + System.out.println("file length = " + f.length()); + } + + static void createJavaFile() throws IOException { + FileOutputStream fos = null; + try { + fos = new FileOutputStream(TEST2_JAVA); + PrintStream ps = new PrintStream(fos); + ps.println("public class " + TEST2_FILE_NAME + + " {" + TEST_FILE_NAME + " b = new " + + TEST_FILE_NAME + " ();}"); + } finally { + fos.close(); + } + } + + public static void main(String... args) throws Exception { + createClassFile(); + enlargeClassFile(); + createJavaFile(); + Thread t = new Thread () { + @Override + public void run() { + String cmds[] = {"-verbose", "-cp", ".", TEST2_JAVA}; + int ret = com.sun.tools.javac.Main.compile(cmds); + System.out.println("test compilation returns: " + ret); + } + }; + t.start(); + t.join(1000*10); + System.out.println(t.getState()); + if (t.isAlive()) { + throw new RuntimeException("Error: compilation is looping"); + } + } +}