# HG changeset patch # User ksrini # Date 1292693919 28800 # Node ID 5de9ea98089d0a9c580ba3c6c33e9b06dd20e7bc # Parent b022f7b7834218d25e604d5a06e1da15589d428b 6567415: Neverending loop in ClassReader Reviewed-by: jjg diff -r b022f7b78342 -r 5de9ea98089d langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java --- a/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Wed Dec 15 06:39:51 2010 -0800 +++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/ClassReader.java Sat Dec 18 09:38:39 2010 -0800 @@ -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 @@ 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 @@ /** 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 @@ } } } + /* + * 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 -r b022f7b78342 -r 5de9ea98089d langtools/test/tools/javac/6567415/T6567415.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/6567415/T6567415.java Sat Dec 18 09:38:39 2010 -0800 @@ -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"); + } + } +}