Repository: netty/netty
Issue: Jdk sslhandler have blocking call

I tried to integrate some reactive tests with miku-mysql and blockhound (which uses netty-handler under the hood), and noticed that there's a blocking call when using jdkssl implementation (leading to noclassdeffound in the end).
With the netty version embedded in spring-boot-webflux 2.6.7 (tried to upgrade netty-handler version, with no more luck).

What info do you need?

Bests

Issue comments:
What does blockhound log? We'll need the stack trace for that blocking call.
```
reactor.blockhound.BlockingOperationError: Blocking call! java.io.FileInputStream#readBytes

0 = {StackTraceElement@17697} "java.base/java.io.FileInputStream.readBytes(FileInputStream.java)"
1 = {StackTraceElement@17698} "java.base/java.io.FileInputStream.read(FileInputStream.java:276)"
2 = {StackTraceElement@17699} "java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:244)"
3 = {StackTraceElement@17700} "java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:263)"
4 = {StackTraceElement@17701} "java.base/sun.security.util.DerValue.<init>(DerValue.java:412)"
5 = {StackTraceElement@17702} "java.base/sun.security.util.DerValue.<init>(DerValue.java:459)"
6 = {StackTraceElement@17703} "java.base/sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:2012)"
7 = {StackTraceElement@17704} "java.base/sun.security.util.KeyStoreDelegator.engineLoad(KeyStoreDelegator.java:221)"
8 = {StackTraceElement@17705} "java.base/java.security.KeyStore.load(KeyStore.java:1473)"
9 = {StackTraceElement@17706} "java.base/sun.security.ssl.TrustStoreManager$TrustAnchorManager.loadKeyStore(TrustStoreManager.java:390)"
10 = {StackTraceElement@17707} "java.base/sun.security.ssl.TrustStoreManager$TrustAnchorManager.getTrustedCerts(TrustStoreManager.java:336)"
11 = {StackTraceElement@17708} "java.base/sun.security.ssl.TrustStoreManager.getTrustedCerts(TrustStoreManager.java:57)"
12 = {StackTraceElement@17709} "java.base/sun.security.ssl.TrustManagerFactoryImpl.engineInit(TrustManagerFactoryImpl.java:49)"
13 = {StackTraceElement@17710} "java.base/javax.net.ssl.TrustManagerFactory.init(TrustManagerFactory.java:282)"
14 = {StackTraceElement@17711} "java.base/sun.security.ssl.SSLContextImpl.engineInit(SSLContextImpl.java:94)"
15 = {StackTraceElement@17712} "java.base/javax.net.ssl.SSLContext.init(SSLContext.java:314)"
16 = {StackTraceElement@17713} "io.netty.handler.ssl.JdkSslContext.<clinit>(JdkSslContext.java:75)"
17 = {StackTraceElement@17714} "io.netty.handler.ssl.SslContext.newClientContextInternal(SslContext.java:824)"
18 = {StackTraceElement@17715} "io.netty.handler.ssl.SslContextBuilder.build(SslContextBuilder.java:611)"
19 = {StackTraceElement@17716} "reactor.netty.tcp.SslProvider.<init>(SslProvider.java:350)"
20 = {StackTraceElement@17717} "reactor.netty.tcp.SslProvider$Build.build(SslProvider.java:734)"
21 = {StackTraceElement@17718} "dev.miku.r2dbc.mysql.client.SslBridgeHandler.buildProvider(SslBridgeHandler.java:161)"
22 = {StackTraceElement@17719} "dev.miku.r2dbc.mysql.client.SslBridgeHandler.handleSslState(SslBridgeHandler.java:135)"
23 = {StackTraceElement@17720} "dev.miku.r2dbc.mysql.client.SslBridgeHandler.userEventTriggered(SslBridgeHandler.java:86)"
24 = {StackTraceElement@17721} "io.netty.channel.AbstractChannelHandlerContext.invokeUserEventTriggered(AbstractChannelHandlerContext.java:346)"
25 = {StackTraceElement@17722} "io.netty.channel.AbstractChannelHandlerContext.invokeUserEventTriggered(AbstractChannelHandlerContext.java:332)"
26 = {StackTraceElement@17723} "io.netty.channel.AbstractChannelHandlerContext.fireUserEventTriggered(AbstractChannelHandlerContext.java:324)"
27 = {StackTraceElement@17724} "io.netty.channel.DefaultChannelPipeline$HeadContext.userEventTriggered(DefaultChannelPipeline.java:1428)"
28 = {StackTraceElement@17725} "io.netty.channel.AbstractChannelHandlerContext.invokeUserEventTriggered(AbstractChannelHandlerContext.java:346)"
29 = {StackTraceElement@17726} "io.netty.channel.AbstractChannelHandlerContext.invokeUserEventTriggered(AbstractChannelHandlerContext.java:332)"
30 = {StackTraceElement@17727} "io.netty.channel.DefaultChannelPipeline.fireUserEventTriggered(DefaultChannelPipeline.java:913)"
31 = {StackTraceElement@17728} "dev.miku.r2dbc.mysql.client.MessageDuplexCodec.write(MessageDuplexCodec.java:118)"
32 = {StackTraceElement@17729} "io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:717)"
33 = {StackTraceElement@17730} "io.netty.channel.AbstractChannelHandlerContext.invokeWriteAndFlush(AbstractChannelHandlerContext.java:764)"
34 = {StackTraceElement@17731} "io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:790)"
35 = {StackTraceElement@17732} "io.netty.channel.AbstractChannelHandlerContext.writeAndFlush(AbstractChannelHandlerContext.java:758)"
36 = {StackTraceElement@17733} "io.netty.channel.AbstractChannelHandlerContext.writeAndFlush(AbstractChannelHandlerContext.java:808)"
37 = {StackTraceElement@17734} "io.netty.channel.DefaultChannelPipeline.writeAndFlush(DefaultChannelPipeline.java:1025)"
38 = {StackTraceElement@17735} "io.netty.channel.AbstractChannel.writeAndFlush(AbstractChannel.java:306)"
39 = {StackTraceElement@17736} "reactor.netty.channel.ChannelOperations.lambda$sendObject$2(ChannelOperations.java:312)"
40 = {StackTraceElement@17737} "reactor.netty.FutureMono$DeferredFutureMono.subscribe(FutureMono.java:100)"
41 = {StackTraceElement@17738} "reactor.core.publisher.Mono.subscribe(Mono.java:4400)"
42 = {StackTraceElement@17739} "reactor.netty.NettyOutbound.subscribe(NettyOutbound.java:336)"
43 = {StackTraceElement@17740} "reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:451)"
44 = {StackTraceElement@17741} "reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onNext(FluxConcatMap.java:251)"
45 = {StackTraceElement@17742} "reactor.core.publisher.EmitterProcessor.drain(EmitterProcessor.java:491)"
46 = {StackTraceElement@17743} "reactor.core.publisher.EmitterProcessor.tryEmitNext(EmitterProcessor.java:299)"
47 = {StackTraceElement@17744} "reactor.core.publisher.InternalManySink.emitNext(InternalManySink.java:27)"
48 = {StackTraceElement@17745} "reactor.core.publisher.EmitterProcessor.onNext(EmitterProcessor.java:265)"
49 = {StackTraceElement@17746} "dev.miku.r2dbc.mysql.client.ReactorNettyClient.lambda$null$7(ReactorNettyClient.java:174)"
50 = {StackTraceElement@17747} "reactor.core.publisher.LambdaSubscriber.onNext(LambdaSubscriber.java:160)"
51 = {StackTraceElement@17748} "org.springframework.security.test.context.support.ReactorContextTestExecutionListener$DelegateTestExecutionListener$SecuritySubContext.onNext(ReactorContextTestExecutionListener.java:120)"
52 = {StackTraceElement@17749} "reactor.core.publisher.EmitterProcessor.drain(EmitterProcessor.java:491)"
53 = {StackTraceElement@17750} "reactor.core.publisher.EmitterProcessor.tryEmitNext(EmitterProcessor.java:299)"
54 = {StackTraceElement@17751} "reactor.core.publisher.InternalManySink.emitNext(InternalManySink.java:27)"
55 = {StackTraceElement@17752} "reactor.core.publisher.EmitterProcessor.onNext(EmitterProcessor.java:265)"
56 = {StackTraceElement@17753} "dev.miku.r2dbc.mysql.InitHandler.accept(QueryFlow.java:605)"
57 = {StackTraceElement@17754} "dev.miku.r2dbc.mysql.InitHandler.accept(QueryFlow.java:545)"
58 = {StackTraceElement@17755} "reactor.core.publisher.FluxHandleFuseable$HandleFuseableSubscriber.onNext(FluxHandleFuseable.java:176)"
59 = {StackTraceElement@17756} "reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onNext(FluxOnAssembly.java:539)"
60 = {StackTraceElement@17757} "reactor.core.publisher.FluxPeekFuseable$PeekFuseableConditionalSubscriber.onNext(FluxPeekFuseable.java:503)"
61 = {StackTraceElement@17758} "reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onNext(FluxOnAssembly.java:539)"
62 = {StackTraceElement@17759} "reactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onNext(MonoFlatMapMany.java:250)"
63 = {StackTraceElement@17760} "org.springframework.security.test.context.support.ReactorContextTestExecutionListener$DelegateTestExecutionListener$SecuritySubContext.onNext(ReactorContextTestExecutionListener.java:120)"
64 = {StackTraceElement@17761} "reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onNext(FluxOnAssembly.java:539)"
65 = {StackTraceElement@17762} "reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onNext(FluxPeekFuseable.java:210)"
66 = {StackTraceElement@17763} "reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onNext(FluxOnAssembly.java:539)"
67 = {StackTraceElement@17764} "reactor.core.publisher.FluxHandleFuseable$HandleFuseableSubscriber.onNext(FluxHandleFuseable.java:191)"
68 = {StackTraceElement@17765} "reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onNext(FluxOnAssembly.java:539)"
69 = {StackTraceElement@17766} "reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onNext(FluxPeekFuseable.java:854)"
70 = {StackTraceElement@17767} "reactor.core.publisher.EmitterProcessor.drain(EmitterProcessor.java:491)"
71 = {StackTraceElement@17768} "reactor.core.publisher.EmitterProcessor.tryEmitNext(EmitterProcessor.java:299)"
72 = {StackTraceElement@17769} "reactor.core.publisher.InternalManySink.emitNext(InternalManySink.java:27)"
73 = {StackTraceElement@17770} "reactor.core.publisher.EmitterProcessor.onNext(EmitterProcessor.java:265)"
74 = {StackTraceElement@17771} "dev.miku.r2dbc.mysql.client.ReactorNettyClient$ResponseSink.next(ReactorNettyClient.java:340)"
75 = {StackTraceElement@17772} "dev.miku.r2dbc.mysql.client.ReactorNettyClient.lambda$new$0(ReactorNettyClient.java:103)"
76 = {StackTraceElement@17773} "reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:185)"
77 = {StackTraceElement@17774} "reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:279)"
78 = {StackTraceElement@17775} "reactor.netty.channel.FluxReceive.onInboundNext(FluxReceive.java:388)"
79 = {StackTraceElement@17776} "reactor.netty.channel.ChannelOperations.onInboundNext(ChannelOperations.java:404)"
80 = {StackTraceElement@17777} "reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:93)"
81 = {StackTraceElement@17778} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)"
82 = {StackTraceElement@17779} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)"
83 = {StackTraceElement@17780} "io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)"
84 = {StackTraceElement@17781} "dev.miku.r2dbc.mysql.client.MessageDuplexCodec.handleDecoded(MessageDuplexCodec.java:187)"
85 = {StackTraceElement@17782} "dev.miku.r2dbc.mysql.client.MessageDuplexCodec.channelRead(MessageDuplexCodec.java:95)"
86 = {StackTraceElement@17783} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)"
87 = {StackTraceElement@17784} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)"
88 = {StackTraceElement@17785} "io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)"
89 = {StackTraceElement@17786} "io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:327)"
90 = {StackTraceElement@17787} "io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:299)"
91 = {StackTraceElement@17788} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)"
92 = {StackTraceElement@17789} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)"
93 = {StackTraceElement@17790} "io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)"
94 = {StackTraceElement@17791} "io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)"
95 = {StackTraceElement@17792} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)"
96 = {StackTraceElement@17793} "io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)"
97 = {StackTraceElement@17794} "io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)"
98 = {StackTraceElement@17795} "io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)"
99 = {StackTraceElement@17796} "io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:722)"
```

Pull request review comments:
```suggestion
            List<String> ciphersNonTLSv13 = new ArrayList<String>(defaultCiphers);
```
```suggestion
            Set<String> suppertedCiphersNonTLSv13 = new LinkedHashSet<String>(supportedCiphers);
```

Patch that resolved the issue:
diff --git a/common/src/main/java/io/netty/util/internal/Hidden.java b/common/src/main/java/io/netty/util/internal/Hidden.java
index 78c45f715db..5ebf8e780e2 100644
--- a/common/src/main/java/io/netty/util/internal/Hidden.java
+++ b/common/src/main/java/io/netty/util/internal/Hidden.java
@@ -108,6 +108,10 @@ public void applyTo(BlockHound.Builder builder) {
                     "io.netty.handler.ssl.ReferenceCountedOpenSslClientContext$ExtendedTrustManagerVerifyCallback",
                     "verify");
 
+            builder.allowBlockingCallsInside(
+                    "io.netty.handler.ssl.JdkSslContext$Defaults",
+                    "init");
+
             // Let's whitelist SSLEngineImpl.unwrap(...) for now as it may fail otherwise for TLS 1.3.
             // See https://mail.openjdk.java.net/pipermail/security-dev/2020-August/022271.html
             builder.allowBlockingCallsInside(
diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java
index a452924829a..516ef660ba9 100644
--- a/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java
+++ b/handler/src/main/java/io/netty/handler/ssl/JdkSslContext.java
@@ -69,33 +69,54 @@ public class JdkSslContext extends SslContext {
     private static final Provider DEFAULT_PROVIDER;
 
     static {
-        SSLContext context;
-        try {
-            context = SSLContext.getInstance(PROTOCOL);
-            context.init(null, null, null);
-        } catch (Exception e) {
-            throw new Error("failed to initialize the default SSL context", e);
+        Defaults defaults = new Defaults();
+        defaults.init();
+
+        DEFAULT_PROVIDER = defaults.defaultProvider;
+        DEFAULT_PROTOCOLS = defaults.defaultProtocols;
+        SUPPORTED_CIPHERS = defaults.supportedCiphers;
+        DEFAULT_CIPHERS = defaults.defaultCiphers;
+        DEFAULT_CIPHERS_NON_TLSV13 = defaults.defaultCiphersNonTLSv13;
+        SUPPORTED_CIPHERS_NON_TLSV13 = defaults.supportedCiphersNonTLSv13;
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Default protocols (JDK): {} ", Arrays.asList(DEFAULT_PROTOCOLS));
+            logger.debug("Default cipher suites (JDK): {}", DEFAULT_CIPHERS);
         }
+    }
 
-        DEFAULT_PROVIDER = context.getProvider();
+    private static final class Defaults {
+        String[] defaultProtocols;
+        List<String> defaultCiphers;
+        List<String> defaultCiphersNonTLSv13;
+        Set<String> supportedCiphers;
+        Set<String> supportedCiphersNonTLSv13;
+        Provider defaultProvider;
 
-        SSLEngine engine = context.createSSLEngine();
-        DEFAULT_PROTOCOLS = defaultProtocols(context, engine);
+        void init() {
+            SSLContext context;
+            try {
+                context = SSLContext.getInstance(PROTOCOL);
+                context.init(null, null, null);
+            } catch (Exception e) {
+                throw new Error("failed to initialize the default SSL context", e);
+            }
 
-        SUPPORTED_CIPHERS = Collections.unmodifiableSet(supportedCiphers(engine));
-        DEFAULT_CIPHERS = Collections.unmodifiableList(defaultCiphers(engine, SUPPORTED_CIPHERS));
+            defaultProvider = context.getProvider();
 
-        List<String> ciphersNonTLSv13 = new ArrayList<String>(DEFAULT_CIPHERS);
-        ciphersNonTLSv13.removeAll(Arrays.asList(SslUtils.DEFAULT_TLSV13_CIPHER_SUITES));
-        DEFAULT_CIPHERS_NON_TLSV13 = Collections.unmodifiableList(ciphersNonTLSv13);
+            SSLEngine engine = context.createSSLEngine();
+            defaultProtocols = defaultProtocols(context, engine);
 
-        Set<String> suppertedCiphersNonTLSv13 = new LinkedHashSet<String>(SUPPORTED_CIPHERS);
-        suppertedCiphersNonTLSv13.removeAll(Arrays.asList(SslUtils.DEFAULT_TLSV13_CIPHER_SUITES));
-        SUPPORTED_CIPHERS_NON_TLSV13 = Collections.unmodifiableSet(suppertedCiphersNonTLSv13);
+            supportedCiphers = Collections.unmodifiableSet(supportedCiphers(engine));
+            defaultCiphers = Collections.unmodifiableList(defaultCiphers(engine, supportedCiphers));
 
-        if (logger.isDebugEnabled()) {
-            logger.debug("Default protocols (JDK): {} ", Arrays.asList(DEFAULT_PROTOCOLS));
-            logger.debug("Default cipher suites (JDK): {}", DEFAULT_CIPHERS);
+            List<String> ciphersNonTLSv13 = new ArrayList<String>(defaultCiphers);
+            ciphersNonTLSv13.removeAll(Arrays.asList(SslUtils.DEFAULT_TLSV13_CIPHER_SUITES));
+            defaultCiphersNonTLSv13 = Collections.unmodifiableList(ciphersNonTLSv13);
+
+            Set<String> suppertedCiphersNonTLSv13 = new LinkedHashSet<String>(supportedCiphers);
+            suppertedCiphersNonTLSv13.removeAll(Arrays.asList(SslUtils.DEFAULT_TLSV13_CIPHER_SUITES));
+            supportedCiphersNonTLSv13 = Collections.unmodifiableSet(suppertedCiphersNonTLSv13);
         }
     }
 


Test patch: