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

Race condition between adding and removing SslHandler causing NPE #8676

Closed
mingyu89 opened this issue Dec 19, 2018 · 6 comments
Closed

Race condition between adding and removing SslHandler causing NPE #8676

mingyu89 opened this issue Dec 19, 2018 · 6 comments
Assignees
Milestone

Comments

@mingyu89
Copy link

We are seeing the same issue as in ticket #6536 on version 4.1.28.
The variable pendingUnencryptedWrites in SslHandler is initialized in method "handlerAdded" if the following sequence of event happens, we will hit this NPE :

  1. connect channel
  2. add handler to channel
  3. disconnect channel
  4. call SslHandler.handlerRemoved0 from disconnecting channel. This will access pendingUnencryptedWrites and throw NPE
  5. call SslHandler.handlerAdded and initialize pendingUnencryptedWrites. But it's too late.
2018-12-06 15:50:20.473 WARN [nioEventLoopGroup-3-15] io.netty.channel.DefaultChannelPipeline - An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
io.netty.channel.ChannelPipelineException: io.netty.handler.ssl.SslHandler.handlerRemoved() has thrown an exception.
 at io.netty.channel.DefaultChannelPipeline.callHandlerRemoved0(DefaultChannelPipeline.java:676) [netty-all-4.1.28.Final.jar:4.1.28.Final]
 at io.netty.channel.DefaultChannelPipeline.destroyDown(DefaultChannelPipeline.java:922) [netty-all-4.1.28.Final.jar:4.1.28.Final]
 at io.netty.channel.DefaultChannelPipeline.destroyUp(DefaultChannelPipeline.java:888) [netty-all-4.1.28.Final.jar:4.1.28.Final]
 at io.netty.channel.DefaultChannelPipeline.destroy(DefaultChannelPipeline.java:880) [netty-all-4.1.28.Final.jar:4.1.28.Final]
 at io.netty.channel.DefaultChannelPipeline.access$700(DefaultChannelPipeline.java:46) [netty-all-4.1.28.Final.jar:4.1.28.Final]
 at io.netty.channel.DefaultChannelPipeline$HeadContext.channelUnregistered(DefaultChannelPipeline.java:1416) [netty-all-4.1.28.Final.jar:4.1.28.Final]
 at io.netty.channel.AbstractChannelHandlerContext.invokeChannelUnregistered(AbstractChannelHandlerContext.java:181) [netty-all-4.1.28.Final.jar:4.1.28.Final]
 at io.netty.channel.AbstractChannelHandlerContext.invokeChannelUnregistered(AbstractChannelHandlerContext.java:167) [netty-all-4.1.28.Final.jar:4.1.28.Final]
 at io.netty.channel.DefaultChannelPipeline.fireChannelUnregistered(DefaultChannelPipeline.java:865) [netty-all-4.1.28.Final.jar:4.1.28.Final]
 at io.netty.channel.AbstractChannel$AbstractUnsafe$8.run(AbstractChannel.java:830) [netty-all-4.1.28.Final.jar:4.1.28.Final]
 at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163) [netty-all-4.1.28.Final.jar:4.1.28.Final]
 at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404) [netty-all-4.1.28.Final.jar:4.1.28.Final]
 at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:464) [netty-all-4.1.28.Final.jar:4.1.28.Final]
 at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884) [netty-all-4.1.28.Final.jar:4.1.28.Final]
 at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [netty-all-4.1.28.Final.jar:4.1.28.Final]
 at java.lang.Thread.run(Thread.java:748) [?:1.8.0_171]
Caused by: java.lang.NullPointerException
@normanmaurer
Copy link
Member

@mingyu89 I wonder how this can happen... handlerRemoved0 should never be called until handlerAdded was called. Do you call these methods by yourself ? how do you add the SslHandler to the pipeline ? Can you show the code how you bootstrap it ?

@mingyu89
Copy link
Author

@normanmaurer Thank you for looking at it.
The SslHandler isn't added to the pipeline during server bootstrap. After the connection is established, client will start to send requests to server. At some point the client may decide to start to use TLS and (stop sending any other request and) send a certain request telling the server to add SslHandler.
After receiving the request, server will :

  public void enableTLS(final SSLEngine sslEngine)
      throws GeneralSecurityException {
    if (this.attemptedToEnableTLS.compareAndSet(false, true)) {
      _logger.debug("Enabling TLS for transport {}", this.channel);
      final SslHandler handler = new SslHandler(sslEngine, true);
      this.channel.pipeline().addFirst(SSL_HANDLER_STRING, handler);
    } else {
      throw new GeneralSecurityException("TLS can not be enabled on the channel " + this.channel
          + " more than once");
    }
  }

which add the handler to pipleline.
Wouldn't it happen if there's a racing disconnect?

@normanmaurer
Copy link
Member

It should not... is this called from outside the EventLoop ?

@normanmaurer
Copy link
Member

@mingyu89 actually I think I know what happens here... Let me come up with a fix.

normanmaurer added a commit that referenced this issue Dec 22, 2018
…adding the handler to the pipeline.

Motivation:

Due a race in DefaultChannelPipeline / AbstractChannelHandlerContext it was possible to have only handlerRemoved(...) called during tearing down the pipeline, even when handlerAdded(...) was never called. We need to ensure we either call both of none to guarantee a proper lifecycle of the handler.

Modifications:

- Enforce handlerAdded(...) / handlerRemoved(...) semantics / ordering
- Add unit test.

Result:

Fixes #8676 / #6536 .
@normanmaurer
Copy link
Member

@mingyu89 #8684 PTAL

@normanmaurer normanmaurer added this to the 4.1.33.Final milestone Dec 22, 2018
@normanmaurer normanmaurer self-assigned this Dec 23, 2018
@mingyu89
Copy link
Author

mingyu89 commented Jan 2, 2019

@normanmaurer Thank you!

normanmaurer added a commit that referenced this issue Jan 14, 2019
…adding the handler to the pipeline. (#8684)

Motivation:

Due a race in DefaultChannelPipeline / AbstractChannelHandlerContext it was possible to have only handlerRemoved(...) called during tearing down the pipeline, even when handlerAdded(...) was never called. We need to ensure we either call both of none to guarantee a proper lifecycle of the handler.

Modifications:

- Enforce handlerAdded(...) / handlerRemoved(...) semantics / ordering
- Add unit test.

Result:

Fixes #8676 / #6536 .
normanmaurer added a commit that referenced this issue Jan 14, 2019
…adding the handler to the pipeline. (#8684)

Motivation:

Due a race in DefaultChannelPipeline / AbstractChannelHandlerContext it was possible to have only handlerRemoved(...) called during tearing down the pipeline, even when handlerAdded(...) was never called. We need to ensure we either call both of none to guarantee a proper lifecycle of the handler.

Modifications:

- Enforce handlerAdded(...) / handlerRemoved(...) semantics / ordering
- Add unit test.

Result:

Fixes #8676 / #6536 .
cuteant added a commit to cuteant/SpanNetty that referenced this issue Jan 17, 2019
…adding the handler to the pipeline.

Motivation:

Due a race in DefaultChannelPipeline / AbstractChannelHandlerContext it was possible to have only handlerRemoved(...) called during tearing down the pipeline, even when handlerAdded(...) was never called. We need to ensure we either call both of none to guarantee a proper lifecycle of the handler.

Modifications:

- Enforce handlerAdded(...) / handlerRemoved(...) semantics / ordering
- Add unit test.

Result:

Fixes netty/netty#8676 / netty/netty#6536 .
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants