jnr/jnr-unixsocket

After channel close, read and write calls were using recycled file descriptor

Opened this issue · 2 comments

After closing the socket from a different thread, channel write call was successful and writing to a recycled file descriptor belonged to a different connection. Here is the example tested on Linux. I couldn't reproduce the problem with TCP sockets.

import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import jnr.unixsocket.UnixSocketAddress;
import jnr.unixsocket.UnixSocketChannel;

public class CloseTest {

  public static void main(String[] args) {

    try {
      SocketChannel c1 = UnixSocketChannel.open(new UnixSocketAddress("/tmp/j1.sock"));
      // SocketChannel c = SocketChannel.open(new InetSocketAddress("[::1]", 8000));
      c1.write(ByteBuffer.wrap("hello".getBytes("utf-8")));
      c1.close();

      SocketChannel c2 = UnixSocketChannel.open(new UnixSocketAddress("/tmp/j2.sock"));
      c1.write(ByteBuffer.wrap("world".getBytes("utf-8")));
      c2.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Listen on different sockets

nc -Ul /tmp/j1.sock
nc -Ul /tmp/j2.sock

Hmm... so either we or the native IO subsystem is reusing the file descriptor and when we close it we're not properly killing the channel. Seems like marking the channel as closed would work here, right?

Looks like we are not mimicking the SocketChannelImpl logic for checking openness on read and write. They have a number of places where they ensureOpen() and reads and writes check whether input or output have been shut down by looking at a flag. UnixSocketChannel appears to have none of this.

Easy one for someone to contribute... just look at how SocketChannelImpl (in the JDK) handles these openness checks.