jnr / jnr-unixsocket

UNIX domain sockets (AF_UNIX) for Java
Apache License 2.0
280 stars 75 forks source link

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

Open sada-sigsci opened 4 years ago

sada-sigsci commented 4 years ago

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

headius commented 4 years ago

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?

headius commented 4 years ago

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.