Repository: netty/netty
Issue: invalid refernece decrement in AbstractNioByteChannel
Labels: defect

my test server is stop working today, and when i search the logs, i found the following exception:

`<[WARN ]2015-01-29 13:49:50,654,[NioEventLoop], Unexpected exception in the selector loop.
io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
        at io.netty.buffer.AbstractReferenceCountedByteBuf.release(AbstractReferenceCountedByteBuf.java:102) ~[netty-all-4.0.19.Final.jar:4.0.19.Final]
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.handleReadException(AbstractNioByteChannel.java:78) ~[netty-all-4.0.19.Final.jar:4.0.19.Final]
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:157) ~[netty-all-4.0.19.Final.jar:4.0.19.Final]
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:507) ~[netty-all-4.0.19.Final.jar:4.0.19.Final]
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:464) ~[netty-all-4.0.19.Final.jar:4.0.19.Final]>`

it seems a bug in netty, so i dig into the code.
in AbstractNioByteChannel$NioByteUnsafe.read

``` java
   do {
                    byteBuf = allocator.ioBuffer(byteBufCapacity);
                    int writable = byteBuf.writableBytes();
                    int localReadAmount = doReadBytes(byteBuf);
                    if (localReadAmount <= 0) {
                        // not was read release the buffer
                     **   byteBuf.release(); **
                        close = localReadAmount < 0;
                        break;
                    }
```

and in handleReadException()

``` java
 private void handleReadException(ChannelPipeline pipeline,
                                         ByteBuf byteBuf, Throwable cause, boolean close) {
            if (byteBuf != null) {
                if (byteBuf.isReadable()) {
                    setReadPending(false);
                    pipeline.fireChannelRead(byteBuf);
                } else {
                   ** byteBuf.release(); **
                }
            }
            pipeline.fireChannelReadComplete();
            pipeline.fireExceptionCaught(cause);
            if (close || cause instanceof IOException) {
                closeOnRead(pipeline);
            }
        }
```

we should set the byteBuf to null immediately after it was released otherwise it **may** be double freed.  one possible fix is 

``` java
              if (localReadAmount <= 0) {
                        // not was read release the buffer
                        byteBuf.release();
                        byteBuf = null;
                        close = localReadAmount < 0;
                        break;
                    }
```


Issue comments:
@sammychen105 sounds legit... would you mind open a PR with a fix ?

@normanmaurer ok, a PR(#3371) has been submit.

Fixed by #3371


Pull request review comments:

Patch that resolved the issue:
diff --git a/transport/src/main/java/io/netty/channel/nio/AbstractNioByteChannel.java b/transport/src/main/java/io/netty/channel/nio/AbstractNioByteChannel.java
index ccf8f54a821..b4e5b190498 100644
--- a/transport/src/main/java/io/netty/channel/nio/AbstractNioByteChannel.java
+++ b/transport/src/main/java/io/netty/channel/nio/AbstractNioByteChannel.java
@@ -116,6 +116,7 @@ public void read() {
                     if (localReadAmount <= 0) {
                         // not was read release the buffer
                         byteBuf.release();
+                        byteBuf = null;
                         close = localReadAmount < 0;
                         break;
                     }


Test patch: