JDK-4738257 : FileChannel.transferFrom is broken for nested Channel/InputStream
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 1.4.1
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_nt
  • CPU: x86
  • Submitted: 2002-08-28
  • Updated: 2002-11-13
  • Resolved: 2002-10-26
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.
Other
1.4.2 mantisFixed
Description

Name: rmT116609			Date: 08/28/2002


FULL PRODUCT VERSION :
java version "1.4.1-rc"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-rc-b19)
Java HotSpot(TM) Client VM (build 1.4.1-rc-b19, mixed mode)

FULL OPERATING SYSTEM VERSION : Windows NT Version 4.0 SP 6a

A DESCRIPTION OF THE PROBLEM :
When using a nested ReadableByteChannel and InputStream
combination, FileChannel.transferFrom() doesn't work
correctly. Apparently, a byte buffer used in the transfer
is somehow accessed twice, resulting in a
BufferOverflowException.

The example uses a piped channel but the same problem
occurs e.g. with a SocketChannel / ServerSocketChannel
where the object is being read from a socket and another
program or thread is writing to the corresponding server
socket. Thus it might suggest that the problem is related
to FileChannel.transferFrom(), and/or the private inner
class Channels.ReadableByteChannelImpl.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Compile the provided source code (TransferBug.java)
2. Run it, i.e. java TransferBug
3. Observe the output

EXPECTED VERSUS ACTUAL BEHAVIOR :
Expected: nothing is output, the program runs succesfully
Actual: the program fails with an exception


ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.nio.BufferOverflowException
        at java.nio.DirectByteBuffer.put(DirectByteBuffer.java:298)
        at java.nio.channels.Channels$ReadableByteChannelImpl.read
(Channels.java:204)
        at sun.nio.ch.FileChannelImpl.transferFromArbitraryChannel
(FileChannelImpl.java:536)
        at sun.nio.ch.FileChannelImpl.transferFrom(FileChannelImpl.java:574)
        at TransferBug$MyObject.readObject(TransferBug.java:57)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke
(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke
(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:324)
        at java.io.ObjectStreamClass.invokeReadObject
(ObjectStreamClass.java:824)
        at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1746)
        at java.io.ObjectInputStream.readOrdinaryObject
(ObjectInputStream.java:1646)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1274)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:324)
        at TransferBug.main(TransferBug.java:98)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.Serializable;
import java.io.File;
import java.io.RandomAccessFile;
import java.io.BufferedOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.Pipe;

public class TransferBug
{
    private static class MyObject
        implements Serializable
    {
        public MyObject()
            throws IOException
        {
            init();
        }

        private void init()
            throws IOException
        {
            this.file = new File(++MyObject.fileNumber + ".dat");
            this.randomAccessFile = new RandomAccessFile(this.file, "rw");
            this.fileChannel = this.randomAccessFile.getChannel();
        }

        public void setSize(long size)
            throws IOException
        {
            this.randomAccessFile.setLength(size);
        }

        private void writeObject(ObjectOutputStream out)
            throws IOException
        {
            long size = this.fileChannel.size();
            out.writeLong(size);

            this.fileChannel.transferTo(0, size, Channels.newChannel(out));

            out.defaultWriteObject();
        }

        private void readObject(ObjectInputStream in)
            throws IOException, ClassNotFoundException
        {
            init();

            long size = in.readLong();

            setSize(size);

            this.fileChannel.transferFrom(Channels.newChannel(in), 0, size);

            in.defaultReadObject();
        }

        private static volatile int fileNumber = 0;

        private transient File file;
        private transient RandomAccessFile randomAccessFile;
        private transient FileChannel fileChannel;
    }

    public static void main(String[] args)
        throws IOException, ClassNotFoundException
    {
        Pipe pipe = Pipe.open();
        Pipe.SourceChannel sourceChannel = pipe.source();
        final Pipe.SinkChannel sinkChannel = pipe.sink();

        Thread writerThread = new Thread()
        {
            public void run()
            {
                try
                {
                    ObjectOutputStream out = new ObjectOutputStream(new
BufferedOutputStream(Channels.newOutputStream(sinkChannel)));
                    MyObject myObject = new MyObject();
                    myObject.setSize(10000);
                    out.writeObject(myObject);
                    out.flush();
                }
                catch (IOException ioe)
                {
                    throw new RuntimeException(ioe);
                }
            }
        };

        writerThread.start();

        ObjectInputStream in = new ObjectInputStream(Channels.newInputStream
(sourceChannel));
        MyObject myObject = (MyObject) in.readObject();
    }
}

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

(Review ID: 163696) 
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: mantis FIXED IN: mantis INTEGRATED IN: mantis mantis-b05 VERIFIED IN: mantis
14-06-2004

EVALUATION Will examine for 1.4.2. -- ###@###.### 2002/8/29 It was sometimes possible to use the same temp DirectBuffer twice in the same thread. We now use a system of caching temp DirectBuffers and checking them in and out. ###@###.### 2002-10-15
15-10-2002