JDK-8214339 : SSLSocketImpl erroneously wraps SocketException
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.net.ssl
  • Affected Version: 11,12
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: linux
  • CPU: x86_64
  • Submitted: 2018-11-22
  • Updated: 2021-01-06
  • Resolved: 2018-12-15
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 8 Other
11.0.3Fixed 8u261Fixed openjdk8u272Fixed
Related Reports
Relates :  
Relates :  
Description
A DESCRIPTION OF THE PROBLEM :
It appears that the TLS1.3-related work in OpenJDK-11 changed SSLSocketImpl.handleException to wrap underlying SocketExceptions in SSLProtocolExceptions.

Specifically, before TLS1.3 changes, handleException simply rethrows IOExceptions (here: https://github.com/unofficial-openjdk/openjdk/blob/322f09b333edd0d26594c52fab7a5346f7b40578/src/java.base/share/classes/sun/security/ssl/SSLSocketImpl.java#L1895). After TLS1.3 changes, handleException pipes a SocketException into .fatal(..) and then into Alert.UNEXPECTED_MESSAGE.createSslException which ultimately wraps the cause in an SSLProtocolException.

First, this contradicts the SSLProtocolException javadoc which says that an SSLProtocolException "Reports an error in the operation of the SSL protocol. Normally this indicates a flaw in one of the protocol implementations."

Additionally, there's existing, widely used code that relies on SocketExceptions being rethrown here. A good example is Apache HttpClient whose default retry logic excludes all SSLExceptions from being retried (see https://hc.apache.org/httpcomponents-client-ga/httpclient/xref/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.html#L104).

REGRESSION : Last worked in version 10.0.2

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Please find a test that reproduces the issue here https://github.com/ogolberg/java-11-sslcontextimpl-bug

It passes on Java 10- and fails on Java 11.



Comments
Fix Request This is important to have for sane SSL implementation. 11.0.3-oracle has it, 11u should have it too. The patch applies with trivial formatting conflicts to 11u, passes jdk_security and new regression tests. 11u RFR: https://mail.openjdk.java.net/pipermail/jdk-updates-dev/2019-February/000517.html
19-02-2019

It's bad to depends on exception class. But it seems that there is no good way for handshake retry. We may want to fix it in JDK 12.
12-12-2018

To reproduce the issue, run the attached test case JDK 8u191 - Pass JDK 11.0.1 - Fail JDK 12-ea+21 - Fail <failure message="java.lang.Exception: Unexpected exception, expected<java.net.SocketException> but was<javax.net.ssl.SSLProtocolException>" type="java.lang.Exception">java.lang.Exception: Unexpected exception, expected<java.net.SocketException> but was<javax.net.ssl.SSLProtocolException>; at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:28) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:106) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38) at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:66) at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:567) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32) at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93) at com.sun.proxy.$Proxy1.processTestClass(Unknown Source) at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:109) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:567) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:155) at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:137) at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63) at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55) at java.base/java.lang.Thread.run(Thread.java:835) Caused by: javax.net.ssl.SSLProtocolException: Connection reset at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:126) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:321) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264) at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:259) at java.base/sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1313) at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:839) at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:758) at jdk.test.SSLContextImplIntegrationTest.socketExceptionShouldBubbleUp(SSLContextImplIntegrationTest.java:23) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:567) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:19) ... 39 more Caused by: java.net.SocketException: Connection reset at java.base/java.net.SocketInputStream.read(SocketInputStream.java:186) at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140) at java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:448) at java.base/sun.security.ssl.SSLSocketInputRecord.bytesInCompletePacket(SSLSocketInputRecord.java:68) at java.base/sun.security.ssl.SSLSocketImpl.readApplicationRecord(SSLSocketImpl.java:1103) at java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:823) ... 50 more
27-11-2018