JDK-6512111 : final long stack variable gets corrupted when FileChannel read is interrupted
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 6
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2007-01-11
  • Updated: 2011-02-16
  • Resolved: 2007-01-31
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7 Other
6u1Fixed 7Fixed hs10Fixed
Description
FULL PRODUCT VERSION :
1.6.0-b105

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
final long stack variable gets corrupted when FileChannel read is interrupted

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
run the main method in provided source code

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
final long variable value remains constant
ACTUAL -
final long variable value gets corrupted during program execution

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.security.MessageDigest;
import java.util.Date;
import java.util.Random;

public class CorruptFinalLong {

	private static final Random random = new Random();

	private static void createFileIfNeeded(File file) throws IOException {
		if (!file.exists()) {
			int bufSize = 1000000;
			int rounds = 50;
			System.out.println("writing random file " + file + " of size " + (bufSize * rounds)
					+ " bytes");
			byte[] buf = new byte[bufSize];
			OutputStream out = new BufferedOutputStream(new FileOutputStream(file), 65536);
			for (int i = 0; i < rounds; i++) {
				random.nextBytes(buf);
				out.write(buf);
			}
			out.close();
		}
	}

	public static void main(String[] args) throws Exception {

		System.setErr(System.out);

		String[] props = new String[] { "java.vm.version", "java.vm.name", "sun.os.patch.level",
				"java.runtime.version", "java.vm.version", "os.arch", "os.name",
				"java.specification.version", "java.vm.specification.version",
				"java.specification.vendor", "java.version" };

		for (String p : props) {
			System.out.printf("%30s: %s", p, System.getProperty(p));
			System.out.println();
		}
		System.out.println();

		final File file = new File("random.dat");

		createFileIfNeeded(file);

		final Thread t = Thread.currentThread();

		new Thread(new Runnable() {
			public void run() {
				try {
					Thread.sleep(1000);
					System.out.println("\tabout to interrupt thread '" + t + "' from thread '"
							+ Thread.currentThread() + "'");
					t.interrupt();
				} catch (InterruptedException e) {
					System.out.println("\tinterrupted in thread");
					e.printStackTrace();
				}

			}
		}).start();

		final long start = System.currentTimeMillis();
		System.out.println("started at " + new Date(start) + " (" + start + " ms)");
		System.out.println();

		MessageDigest md = MessageDigest.getInstance("MD5");

		ByteChannel channel = new FileInputStream(file).getChannel();
		try {
			int cap = 10;
			ByteBuffer byteBuffer = ByteBuffer.allocateDirect(cap);
			byte[] local = new byte[cap];
			long lastRead = 0;
			boolean eof = false;
			while (!eof) {
				byteBuffer.rewind();
				int bytesReadThisTime = channel.read(byteBuffer);
				if (bytesReadThisTime == -1) {
					eof = true;
				} else {
					// totalRead += bytesReadThisTime;
					byteBuffer.rewind();
					byteBuffer.get(local, 0, bytesReadThisTime);
					md.update(local, 0, bytesReadThisTime);
					if (System.currentTimeMillis() - lastRead > 1000) {
						lastRead = System.currentTimeMillis();
					}
				}
			}
		} catch (Exception e) {
			if (Thread.interrupted()) {
				System.out.println("\t********* IO was interrupted in thread '"
						+ Thread.currentThread() + "': " + e);
			} else {
				System.out.println("\texception: " + e);
			}
		} finally {
			channel.close();
		}

		System.out.println();
		System.out
				.println("the following line should be identical to the similar one above, since variable 'start' is final:");
		System.out.println("started at " + new Date(start) + " (" + start + " ms)");

	}

}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
none --- aside from reverting to java 5.0

Comments
EVALUATION Note that this only effect x86.
17-01-2007

SUGGESTED FIX Job ID: 20070117123341.never.6512111 Original workspace: smite:/export/ws/6512111 Submitter: never Archived data: /net/prt-archiver.sfbay/data/archived_workspaces/main/c2_baseline/2007/20070117123341.never.6512111/ Webrev: http://prt-web.sfbay.sun.com/net/prt-archiver.sfbay/data/archived_workspaces/main/c2_baseline/2007/20070117123341.never.6512111/workspace/webrevs/webrev-2007.01.17/index.html Fixed 6512111: final long stack variable gets corrupted when FileChannel read is interrupted The code for emitting stack to stack moves with long or double values wasn't being emitted correctly which resulted in corrupted locals when such a move was needed along an exception edge. Exception edges are really the only place where stack to stack moves are emitted so that's why it only showed up here. This is being putback to jdk7 and also being proposed for 6u1 so the review would be for both putbacks. http://javaweb.sfbay/~never/webrev/6512111 Approved by: Reviewed by: kvn, jrose Fix verified (y/n): y test case
11-01-2007

EVALUATION The problem is actually in the generation of exception adapters. Exception edges aren't explicitly represented in the control flow graph so adapters are inserted along the exception edges to adjust the register states between the catch and throw points. Sometimes they have to emit stack to stack moves and the code for doing that wasn't accounting for the fact that the push changes the esp so it was picking up the wrong high word in the move. The fix is trivial.
11-01-2007

EVALUATION It looks like it's a bug with OSR and longs.
11-01-2007

EVALUATION Possible compiler1 issue on x86 rather than NIO bug.
11-01-2007

WORK AROUND Use Server VM (-server option).
11-01-2007