Repository navigation
Tornado 4.5.3 hangs with openssl 1.1.1 and TLS 1.3 #2536
Copy link
Copy link
Closed
Labels
Description
I want to package the Python 3 version of tornado 4.5.3 (the latest 4.x release) for Debian unstable, because salt does not work with tornado 5 yet. See https://github.com/saltstack/salt-jenkins/issues/995 for details.
I updated the test certificate and tweaked the source code to work with Python 3.7:
- https://salsa.debian.org/python-team/modules/python-tornado/commit/2c6f99ca6c18d5934a97fb9e6d5d4fa9030efe42
- https://salsa.debian.org/python-team/modules/python-tornado/commit/19e541067a64b451ee31b7a3400c48ef61ec082d
Sadly the a few test cases still fail on Debian unstable (they succeed on Ubuntu 18.04):
======================================================================
ERROR: test_inline_read_error (tornado.test.iostream_test.TestIOStreamSSL)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/building/package/tornado/test/iostream_test.py", line 556, in test_inline_read_error
server.read_bytes(1, lambda data: None)
File "/usr/lib/python3.7/unittest/case.py", line 203, in __exit__
self._raiseFailure("{} not raised".format(exc_name))
File "/usr/lib/python3.7/unittest/case.py", line 135, in _raiseFailure
raise self.test_case.failureException(msg)
AssertionError: OSError not raised
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/tmp/building/package/tornado/testing.py", line 136, in __call__
result = self.orig_method(*args, **kwargs)
File "/tmp/building/package/tornado/test/iostream_test.py", line 558, in test_inline_read_error
server.close()
File "/tmp/building/package/tornado/iostream.py", line 444, in close
self.close_fd()
File "/tmp/building/package/tornado/iostream.py", line 1042, in close_fd
self.socket.close()
File "/usr/lib/python3.7/socket.py", line 420, in close
self._real_close()
File "/usr/lib/python3.7/ssl.py", line 1108, in _real_close
super()._real_close()
File "/usr/lib/python3.7/socket.py", line 414, in _real_close
_ss.close(self)
OSError: [Errno 9] Bad file descriptor
======================================================================
ERROR: test_inline_read_error (tornado.test.iostream_test.TestIOStreamSSLContext)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/building/package/tornado/test/iostream_test.py", line 556, in test_inline_read_error
server.read_bytes(1, lambda data: None)
File "/usr/lib/python3.7/unittest/case.py", line 203, in __exit__
self._raiseFailure("{} not raised".format(exc_name))
File "/usr/lib/python3.7/unittest/case.py", line 135, in _raiseFailure
raise self.test_case.failureException(msg)
AssertionError: OSError not raised
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/tmp/building/package/tornado/testing.py", line 136, in __call__
result = self.orig_method(*args, **kwargs)
File "/tmp/building/package/tornado/test/iostream_test.py", line 558, in test_inline_read_error
server.close()
File "/tmp/building/package/tornado/iostream.py", line 444, in close
self.close_fd()
File "/tmp/building/package/tornado/iostream.py", line 1042, in close_fd
self.socket.close()
File "/usr/lib/python3.7/socket.py", line 420, in close
self._real_close()
File "/usr/lib/python3.7/ssl.py", line 1108, in _real_close
super()._real_close()
File "/usr/lib/python3.7/socket.py", line 414, in _real_close
_ss.close(self)
OSError: [Errno 9] Bad file descriptor
======================================================================
FAIL: test_read_until_close_after_close (tornado.test.iostream_test.TestIOStreamSSL)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/building/package/tornado/testing.py", line 136, in __call__
result = self.orig_method(*args, **kwargs)
File "/tmp/building/package/tornado/test/iostream_test.py", line 451, in test_read_until_close_after_close
data = self.wait()
File "/tmp/building/package/tornado/testing.py", line 336, in wait
self.__rethrow()
File "/tmp/building/package/tornado/testing.py", line 272, in __rethrow
raise_exc_info(failure)
File "<string>", line 4, in raise_exc_info
File "/tmp/building/package/tornado/testing.py", line 320, in timeout_func
timeout)
AssertionError: Async operation timed out after 5 seconds
======================================================================
FAIL: test_streaming_read_until_close_after_close (tornado.test.iostream_test.TestIOStreamSSL)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/building/package/tornado/testing.py", line 136, in __call__
result = self.orig_method(*args, **kwargs)
File "/tmp/building/package/tornado/test/iostream_test.py", line 481, in test_streaming_read_until_close_after_close
data = self.wait()
File "/tmp/building/package/tornado/testing.py", line 336, in wait
self.__rethrow()
File "/tmp/building/package/tornado/testing.py", line 272, in __rethrow
raise_exc_info(failure)
File "<string>", line 4, in raise_exc_info
File "/tmp/building/package/tornado/testing.py", line 320, in timeout_func
timeout)
AssertionError: Async operation timed out after 5 seconds
======================================================================
FAIL: test_write_zero_bytes (tornado.test.iostream_test.TestIOStreamSSL)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/building/package/tornado/testing.py", line 136, in __call__
result = self.orig_method(*args, **kwargs)
File "/tmp/building/package/tornado/test/iostream_test.py", line 220, in test_write_zero_bytes
self.wait()
File "/tmp/building/package/tornado/testing.py", line 336, in wait
self.__rethrow()
File "/tmp/building/package/tornado/testing.py", line 272, in __rethrow
raise_exc_info(failure)
File "<string>", line 4, in raise_exc_info
File "/tmp/building/package/tornado/testing.py", line 320, in timeout_func
timeout)
AssertionError: Async operation timed out after 5 seconds
======================================================================
FAIL: test_read_until_close_after_close (tornado.test.iostream_test.TestIOStreamSSLContext)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/building/package/tornado/testing.py", line 136, in __call__
result = self.orig_method(*args, **kwargs)
File "/tmp/building/package/tornado/test/iostream_test.py", line 451, in test_read_until_close_after_close
data = self.wait()
File "/tmp/building/package/tornado/testing.py", line 336, in wait
self.__rethrow()
File "/tmp/building/package/tornado/testing.py", line 272, in __rethrow
raise_exc_info(failure)
File "<string>", line 4, in raise_exc_info
File "/tmp/building/package/tornado/testing.py", line 320, in timeout_func
timeout)
AssertionError: Async operation timed out after 5 seconds
======================================================================
FAIL: test_streaming_read_until_close_after_close (tornado.test.iostream_test.TestIOStreamSSLContext)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/building/package/tornado/testing.py", line 136, in __call__
result = self.orig_method(*args, **kwargs)
File "/tmp/building/package/tornado/test/iostream_test.py", line 481, in test_streaming_read_until_close_after_close
data = self.wait()
File "/tmp/building/package/tornado/testing.py", line 336, in wait
self.__rethrow()
File "/tmp/building/package/tornado/testing.py", line 272, in __rethrow
raise_exc_info(failure)
File "<string>", line 4, in raise_exc_info
File "/tmp/building/package/tornado/testing.py", line 320, in timeout_func
timeout)
AssertionError: Async operation timed out after 5 seconds
======================================================================
FAIL: test_write_zero_bytes (tornado.test.iostream_test.TestIOStreamSSLContext)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/building/package/tornado/testing.py", line 136, in __call__
result = self.orig_method(*args, **kwargs)
File "/tmp/building/package/tornado/test/iostream_test.py", line 220, in test_write_zero_bytes
self.wait()
File "/tmp/building/package/tornado/testing.py", line 336, in wait
self.__rethrow()
File "/tmp/building/package/tornado/testing.py", line 272, in __rethrow
raise_exc_info(failure)
File "<string>", line 4, in raise_exc_info
File "/tmp/building/package/tornado/testing.py", line 320, in timeout_func
timeout)
AssertionError: Async operation timed out after 5 seconds
You can see the full build and test long here: https://salsa.debian.org/python-team/modules/python-tornado/-/jobs/77766 (you can ignore the test cases that fail with "Cannot assign requested address")
Reactions are currently unavailable
Activity
bdarnell commentedon Nov 21, 2018
Hmm, I wonder what changed. Debian unstable had a python3-tornado package at version 4.5.3 until March of this year. I assume it passed its tests then.
The test failures are all in SSL tests, and it looks like openssl 1.1 is in use. #2533 reports that openssl 1.1 appears to change exception behavior that might explain this.
bdarnell commentedon Nov 21, 2018
It doesn't look like openssl 1.1 is at fault (at least not alone). I built python 3.6 with openssl 1.1.0e (using this docker script) and the tests of Tornado 4.5.3 didn't break. There's something else going on here.
bdrung commentedon Dec 11, 2018
Can you test with Python 3.7 again?
bdarnell commentedon Dec 12, 2018
Works for me with my macports-based installation of python 3.7 (well, it doesn't completely work, but it doesn't get those "timed out after 5 seconds" errors).
bdrung commentedon Dec 13, 2018
The tests succeed on Debian 9 (stretch) and Ubuntu 18.04 (bionic), but they fail on Debian unstable, Ubuntu 18.10 (cosmic) and Ubuntu 19.04 (disco).
Build logs
Ubuntu 18.04 (bionic): https://launchpadlibrarian.net/401407619/buildlog_ubuntu-bionic-amd64.python-tornado4_4.5.3-2~wip20181213+tests~ubuntu18.04.1~ppa1_BUILDING.txt.gz
Ubuntu 18.10 (cosmic): https://launchpadlibrarian.net/401407395/buildlog_ubuntu-cosmic-amd64.python-tornado4_4.5.3-2~wip20181213+tests~ubuntu18.10.1~ppa1_BUILDING.txt.gz
Ubuntu 19.04 (disco): https://launchpadlibrarian.net/401407363/buildlog_ubuntu-disco-amd64.python-tornado4_4.5.3-2~wip20181213+tests~ubuntu19.04.1~ppa1_BUILDING.txt.gz
bdarnell commentedon Dec 15, 2018
OK, I can reproduce this now: it appears that the key is openssl version 1.1.1 (not 1.1.0e, which I had tried, or 1.1.0g in bionic). If I build Python 3.7 with openssl 1.1.1a, the Tornado 4.5.3 tests fail with the 5-second timeout errors. Those errors don't occur with Tornado 5.0.0.
I don't think there were any changes specific to SSL in 5.0, although substantial parts of the relevant code in iostream.py was rewritten for performance. Openssl 1.1.1 claims to be a drop-in replacement for 1.1.0 and I don't see anything obviously relevant in the release notes (it's possible that the offending change was actually in one of the later 1.1.0 releases; I haven't tried 1.1.0h or 1.1.0i)
bdarnell commentedon Dec 15, 2018
Ah wait, there's this:
https://github.com/openssl/openssl/blob/6e94b5aecd619afd25e3dc25902952b1b3194edf/CHANGES#L236
Although that comment specifically refers to blocking IO and it looks like python has set this flag by default for a long time.
Or there's openssl/openssl#7327, suggesting that when TLS 1.3 is used the pattern of messages is different in ways that could potentially confuse socket reading loops. And indeed, adding
ctx.maximum_version = ssl.TLSVersion.TLSv1_2wherever anSSLContextis created eliminates the timeouts.So I think that's the answer for now: disable TLS 1.3 when using Tornado 4.5 with openssl 1.1.1. Since this is apparently already fixed in 5.0+, I'm not going to personally investigate further on the old release branch (but I'd welcome a patch if there are folks out there who still can't upgrade to 5.x but also can't pin their openssl to a similar vintage).
bdrung commentedon Dec 17, 2018
It would be great to use TLS 1.3 with tornado 4, because TLS 1.3 would improve the security. Could you point me to the code part where to look for finding the relevant commit to cherry-pick it?
Skip failing openssl 1.1.1 tests (for now)
Skip failing openssl 1.1.1 tests (for now)
bdrung commentedon Dec 18, 2018
There is one salt state unittest (unit.utils.test_event.TestAsyncEventPublisher.test_event_subscription) that fails with exactly this failure.
bdarnell commentedon Dec 19, 2018
There were a lot of changes to iostream.py between 4.5 and 5.0. It could be an unexpected side effect of some of the new zero-copy fast paths introduced in #2169 or #2251. Or there were some SSL-related changes in #2167 and #2177.
bdrung commentedon Dec 20, 2018
I tested the two SSL-related changes in #2167 and #2177. They made no difference. Also the patch from #2169 didn't change anything. #2251 does not apply and is not trivially adaptable.
18 remaining items
Skip failing openssl 1.1.1 tests (for now)
PR (#1)
Skip failing openssl 1.1.1 tests (for now)
test: Disable TLS 1.3 in one test
bdarnell commentedon Aug 28, 2022
Closing this as obsolete since it seems to only apply to Tornado 4.x.
Skip failing openssl 1.1.1 tests (for now)