Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NettyAdaptiveCumulator incorrectly releases ByteBuf in twice #10255

Closed
hengyunabc opened this issue Jun 7, 2023 · 2 comments · Fixed by #10537
Closed

NettyAdaptiveCumulator incorrectly releases ByteBuf in twice #10255

hengyunabc opened this issue Jun 7, 2023 · 2 comments · Fixed by #10537
Assignees
Labels
Milestone

Comments

@hengyunabc
Copy link

What version of gRPC-Java are you using?

Version 1.51.0 introduced this issue.

What is your environment?

Any

What did you expect to see?

What did you see instead?

Steps to reproduce the bug

io.netty.handler.codec.DecoderException: io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:294)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
	at io.netty.channel.local.LocalChannel.readInbound(LocalChannel.java:299)
	at io.netty.channel.local.LocalChannel.doBeginRead(LocalChannel.java:322)
	at io.netty.channel.AbstractChannel$AbstractUnsafe.beginRead(AbstractChannel.java:834)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.read(DefaultChannelPipeline.java:1362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeRead(AbstractChannelHandlerContext.java:835)
	at io.netty.channel.AbstractChannelHandlerContext.read(AbstractChannelHandlerContext.java:814)
	at io.netty.handler.codec.http2.Http2ConnectionHandler.read(Http2ConnectionHandler.java:539)
	at io.netty.channel.AbstractChannelHandlerContext.invokeRead(AbstractChannelHandlerContext.java:839)
	at io.netty.channel.AbstractChannelHandlerContext.read(AbstractChannelHandlerContext.java:814)
	at io.netty.channel.DefaultChannelPipeline.read(DefaultChannelPipeline.java:1004)
	at io.netty.channel.AbstractChannel.read(AbstractChannel.java:290)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.readIfIsAutoRead(DefaultChannelPipeline.java:1422)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelReadComplete(DefaultChannelPipeline.java:1417)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelReadComplete(AbstractChannelHandlerContext.java:482)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelReadComplete(AbstractChannelHandlerContext.java:463)
	at io.netty.channel.DefaultChannelPipeline.fireChannelReadComplete(DefaultChannelPipeline.java:925)
	at io.netty.channel.local.LocalChannel.readInbound(LocalChannel.java:302)
	at io.netty.channel.local.LocalChannel.finishPeerRead0(LocalChannel.java:445)
	at io.netty.channel.local.LocalChannel.access$400(LocalChannel.java:50)
	at io.netty.channel.local.LocalChannel$5.run(LocalChannel.java:403)
	at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174)
	at io.netty.channel.DefaultEventLoop.run(DefaultEventLoop.java:54)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:748)
Caused by: io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
	at io.netty.util.internal.ReferenceCountUpdater.toLiveRealRefCnt(ReferenceCountUpdater.java:83)
	at io.netty.util.internal.ReferenceCountUpdater.release(ReferenceCountUpdater.java:147)
	at io.netty.buffer.AbstractReferenceCountedByteBuf.release(AbstractReferenceCountedByteBuf.java:101)
	at io.grpc.netty.NettyAdaptiveCumulator.cumulate(NettyAdaptiveCumulator.java:95)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:288)
	... 33 common frames omitted

In the case below, NettyAdaptiveCumulator incorrectly releases ByteBuf in twice.

  • io.grpc.netty.NettyAdaptiveCumulator.cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in)

  • cumulation: UnpooledHeapByteBuf(ridx: 9, widx: 14, cap: 14/14)

  • in: UnpooledHeapByteBuf(ridx: 0, widx: 8, cap: 8/8)


  • The finally block of NettyAdaptiveCumulator#mergeWithCompositeTail is released once
  • Release again in the finally block of NettyAdaptiveCumulator#cumulate
@sanjaypujare
Copy link
Contributor

@sergiitk may be you can take a look?

@larry-safran
Copy link
Contributor

It looks to me that there is one (and only one) way a double release could happen. It is if an exception was thrown in line 211 of NettyAdaptiveCumulator.java as mergeWithCompositeTail only has that line of logic after in.release() on line 207.
This is because after the call to addInput calls mergeWithCompositeTail it immediately sets in = null and the release of in is gated by in != null. However, composite.readerIndex calls AbstractBuffer.readerIndex which can do a checkIndexBounds (default behavior) which can throw an IndexOutOfBoundsException.

It would be good to change

      if (in != null) {
        in.release();
      }

to

       if (in != null && in.refCnt() > 0) {
        in.release();
      }

@larry-safran larry-safran added this to the Next milestone Aug 21, 2023
@sergiitk sergiitk assigned larry-safran and unassigned sergiitk Sep 5, 2023
@sergiitk sergiitk removed this from the Next milestone Sep 5, 2023
@sergiitk sergiitk added this to the Next milestone Sep 13, 2023
@ejona86 ejona86 modified the milestones: Next, 1.59 Nov 13, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 12, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
5 participants