United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: 6512111 final long stack variable gets corrupted when FileChannel read is interrupted
6512111 : final long stack variable gets corrupted when FileChannel read is interrupted

Details
Type:
Bug
Submit Date:
2007-01-11
Status:
Resolved
Updated Date:
2011-02-16
Project Name:
JDK
Resolved Date:
2007-01-31
Component:
hotspot
OS:
windows_xp
Sub-Component:
compiler
CPU:
x86
Priority:
P3
Resolution:
Fixed
Affected Versions:
6
Fixed Versions:
hs10

Related Reports
Backport:
Backport:

Sub Tasks

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.
                                     
2007-01-17
EVALUATION

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

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

It looks like it's a bug with OSR and longs.
                                     
2007-01-11
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
                                     
2007-01-11
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.
                                     
2007-01-11



Hardware and Software, Engineered to Work Together