classification
Title: SSLContext.wrap_socket() throws OSError with errno == 0
Type: behavior Stage: resolved
Components: SSL Versions: Python 3.9, Python 3.8, Python 3.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: gregory.p.smith Nosy List: Ben.Darnell, Dima.Tisnek, Safihre, christian.heimes, gregory.p.smith, jab, martin.panter, miss-islington, nikratio, remi.lapeyre, shevis, xgdomingo
Priority: normal Keywords: patch

Created on 2017-08-04 19:16 by nikratio, last changed 2020-08-15 17:45 by miss-islington. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 18772 merged Dima.Tisnek, 2020-03-04 07:25
PR 21888 merged miss-islington, 2020-08-15 17:01
PR 21889 merged miss-islington, 2020-08-15 17:01
Messages (18)
msg299759 - (view) Author: Nikolaus Rath (nikratio) * Date: 2017-08-04 19:16
With a particularly atrocious network connection, I often get the following exception:

  File "/usr/lib/python3/dist-packages/dugong/__init__.py", line 503, in connect
    self._sock = self.ssl_context.wrap_socket(self._sock, server_hostname=server_hostname)
  File "/usr/lib/python3.5/ssl.py", line 385, in wrap_socket
    _context=self)
  File "/usr/lib/python3.5/ssl.py", line 760, in __init__
    self.do_handshake()
  File "/usr/lib/python3.5/ssl.py", line 996, in do_handshake
    self._sslobj.do_handshake()
  File "/usr/lib/python3.5/ssl.py", line 641, in do_handshake
    self._sslobj.do_handshake()
OSError: [Errno 0] Error

I don't think an error with errno == 0 should ever be raised by Python.
msg299766 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2017-08-05 01:49
It might help if you explained what “atrocities” are happening on your network. Is there a proxy or man-in-the-middle (or the remote peer) that shuts down TCP connections?

If so, perhaps this is similar to Issue 10808. From my memory, in that case an OS “recv” or “read” call returns zero to indicate a connection was shut down. Python and/or Open SSL correctly treats this as an error, but then assumes “errno” is valid. Perhaps Python should be raising SSLEOFError in this situation.

Maybe also check the “suppress_ragged_eofs” setting, but I think that only affects later stages, after the handshake succeeds.
msg299793 - (view) Author: Nikolaus Rath (nikratio) * Date: 2017-08-05 20:07
Regarding "atrocious connection": I wish I knew, but I have no control of the connection. All I can tell is that there are frequent disconnects, occasional latency spikes, my remote ip address seems to change frequently (while the apparent local one stays as-is, so presumably some NAT in between), temporary bandwidth drops, etc.
msg328093 - (view) Author: Ben Darnell (Ben.Darnell) * Date: 2018-10-20 00:04
We have an easy reproduction of this "[Errno 0] Error" on the server side in https://github.com/tornadoweb/tornado/issues/2504#issuecomment-426782158

It is triggered by a connection from `nc -z` (which I think is doing a TCP handshake and shutting down the connection cleanly, but I'm not sure. It might just send an RST instead of the clean shutdown). On macos, I get SSL_ERROR_EOF (as expected), but on linux it raises an OSError with errno 0. (Note that the script as posted has a small mistake in that it is using a client-side SSLContext on the server side. The same error is seen when that mistake is fixed) 

I'm going to add "errno 0" to the list of errors that Tornado should swallow silently here, so if you're trying to reproduce this in the future use Tornado 5.1.1.
msg332779 - (view) Author: Gregory P. Smith (gregory.p.smith) * (Python committer) Date: 2018-12-31 01:08
This flakiness just caused a PR merge to be blocked by AppVeyor for me:

======================================================================
ERROR: test_with_statement (test.test_nntplib.NetworkedNNTP_SSLTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\projects\cpython\lib\test\test_nntplib.py", line 242, in wrapped
    meth(self)
  File "C:\projects\cpython\lib\test\test_nntplib.py", line 264, in test_with_statement
    with self.NNTP_CLASS(self.NNTP_HOST, timeout=TIMEOUT, usenetrc=False) as server:
  File "C:\projects\cpython\lib\nntplib.py", line 1077, in __init__
    self.sock = _encrypt_on(self.sock, ssl_context, host)
  File "C:\projects\cpython\lib\nntplib.py", line 292, in _encrypt_on
    return context.wrap_socket(sock, server_hostname=hostname)
  File "C:\projects\cpython\lib\ssl.py", line 405, in wrap_socket
    return self.sslsocket_class._create(
  File "C:\projects\cpython\lib\ssl.py", line 853, in _create
    self.do_handshake()
  File "C:\projects\cpython\lib\ssl.py", line 1117, in do_handshake
    self._sslobj.do_handshake()
OSError: [Errno 0] Error
----------------------------------------------------------------------

https://ci.appveyor.com/project/python/cpython/builds/21299396

I lucked out by kicking it with a no-op change to re-trigger an appveyor run where it passed.
msg336732 - (view) Author: Safihre (Safihre) Date: 2019-02-27 07:04
In the CherryPy project it is also observed on Windows with Python 3,7.2.
In CherryPy it's triggered by Checker plugin, which connects to the app listening to the socket port in TLS mode via plain HTTP during startup (from the same process).
It has been around for a while: https://github.com/cherrypy/cherrypy/issues/1618#issuecomment-454150794
msg363318 - (view) Author: Dima Tisnek (Dima.Tisnek) * Date: 2020-03-04 01:21
I volunteer to test the theory that the connection is closed mid-flight.
msg363322 - (view) Author: Dima Tisnek (Dima.Tisnek) * Date: 2020-03-04 02:17
Rejoice: https://github.com/dimaqq/bpo-31122
Short, easy to reproduce :)
(I've tested on Mac: 3.7, 3.8, 3.9a from python.org, linked against OpenSSL 1.1.1c/d)
Funnily enough, Python 2.7 raises an ssl.SSLEOFError instead 🤷‍♂️
msg363329 - (view) Author: Dima Tisnek (Dima.Tisnek) * Date: 2020-03-04 06:50
I've traced it down to here: https://github.com/python/cpython/blob/be501ca2419a91546dea85ef4f36945545458589/Modules/_ssl.c#L791-L798

`err.c` (errno) == 0, no error, and `err.ssl` == 5, SSL_ERROR_SYSCALL, helpfully commented "look at error stack/return value/errno" in openssl/ssl.h 😅

I'm a bit suspicious about `s->errorhandler()` which is some old convention (git blame: 8 years ago), commented "checks errno, returns NULL, set a Python exception", but at this point, we know that errno is 0, so why call it?

I'm thinking to just change that to SSLEOFError, but I wonder if something else might break?
msg363333 - (view) Author: Dima Tisnek (Dima.Tisnek) * Date: 2020-03-04 07:26
https://github.com/python/cpython/pull/18772 posted
msg364067 - (view) Author: Dima Tisnek (Dima.Tisnek) * Date: 2020-03-13 02:29
If someone can review https://github.com/python/cpython/pull/18772 then pretty-please review 🙏
msg367335 - (view) Author: Dima Tisnek (Dima.Tisnek) * Date: 2020-04-27 00:03
I know Christian is very busy, so what can I do to have this patch reviewed?
* it's concise
* there's a reproducer
msg375471 - (view) Author: Safihre (Safihre) Date: 2020-08-15 15:47
Would anyone be able to review this? People keep reporting this bug in my project.
Months are just passing while the PR is just a few lines of code:
https://github.com/python/cpython/pull/18772/files
msg375478 - (view) Author: miss-islington (miss-islington) Date: 2020-08-15 17:01
New changeset 495bd035662fda29639f9d52bb6baebea31d72fa by Dima Tisnek in branch 'master':
bpo-31122: ssl.wrap_socket() now raises ssl.SSLEOFError rather than OSError when peer closes connection during TLS negotiation (GH-18772)
https://github.com/python/cpython/commit/495bd035662fda29639f9d52bb6baebea31d72fa
msg375479 - (view) Author: Gregory P. Smith (gregory.p.smith) * (Python committer) Date: 2020-08-15 17:03
Thanks!

fyi for confirmation incase anyone doubted:

>>> issubclass(ssl.SSLEOFError, OSError)
True

So from a code point of view, anything already catching the error still catches the error.  100% bugfix.
msg375481 - (view) Author: Gregory P. Smith (gregory.p.smith) * (Python committer) Date: 2020-08-15 17:06
While this is present in 3.7 (and earlier?), 3.7 is EOL - security fix only stage.  the 3.8 and 3.9 PRs should automerge after CI finishes.

please reopen the issue or ping me on those PRs if they somehow fail to do so.
msg375482 - (view) Author: miss-islington (miss-islington) Date: 2020-08-15 17:42
New changeset 243458115e2cb295fb5bbb61e6ac528c6b2cf5be by Miss Islington (bot) in branch '3.8':
bpo-31122: ssl.wrap_socket() now raises ssl.SSLEOFError rather than OSError when peer closes connection during TLS negotiation (GH-18772)
https://github.com/python/cpython/commit/243458115e2cb295fb5bbb61e6ac528c6b2cf5be
msg375484 - (view) Author: miss-islington (miss-islington) Date: 2020-08-15 17:45
New changeset fc8ffe27b6f29d67b76fb2ef57466c95af5a9f82 by Miss Islington (bot) in branch '3.9':
bpo-31122: ssl.wrap_socket() now raises ssl.SSLEOFError rather than OSError when peer closes connection during TLS negotiation (GH-18772)
https://github.com/python/cpython/commit/fc8ffe27b6f29d67b76fb2ef57466c95af5a9f82
History
Date User Action Args
2020-08-15 17:45:01miss-islingtonsetmessages: + msg375484
2020-08-15 17:42:40miss-islingtonsetmessages: + msg375482
2020-08-15 17:06:01gregory.p.smithsetstatus: open -> closed
resolution: fixed
messages: + msg375481

stage: patch review -> resolved
2020-08-15 17:03:05gregory.p.smithsetassignee: christian.heimes -> gregory.p.smith
messages: + msg375479
2020-08-15 17:01:52miss-islingtonsetpull_requests: + pull_request21008
2020-08-15 17:01:46miss-islingtonsetpull_requests: + pull_request21007
2020-08-15 17:01:40miss-islingtonsetnosy: + miss-islington
messages: + msg375478
2020-08-15 15:47:55Safihresetmessages: + msg375471
2020-05-13 00:02:56jabsetnosy: + jab
2020-04-27 08:59:10remi.lapeyresetnosy: + remi.lapeyre
2020-04-27 00:03:30Dima.Tisneksetmessages: + msg367335
2020-03-13 02:29:53Dima.Tisneksetmessages: + msg364067
2020-03-04 07:26:14Dima.Tisneksetmessages: + msg363333
2020-03-04 07:25:53Dima.Tisneksetkeywords: + patch
stage: needs patch -> patch review
pull_requests: + pull_request18130
2020-03-04 06:50:39Dima.Tisneksetmessages: + msg363329
2020-03-04 02:17:23Dima.Tisneksetmessages: + msg363322
versions: + Python 3.9
2020-03-04 01:21:14Dima.Tisneksetmessages: + msg363318
2020-03-03 19:39:23shevissetnosy: + shevis
2019-06-20 00:19:49Dima.Tisneksetnosy: + Dima.Tisnek
2019-02-27 07:04:48Safihresetnosy: + Safihre
messages: + msg336732
2018-12-31 01:10:19gregory.p.smithsetstage: needs patch
2018-12-31 01:09:15gregory.p.smithsetversions: - Python 3.5, Python 3.6
2018-12-31 01:08:59gregory.p.smithsetversions: + Python 3.6, Python 3.7, Python 3.8
2018-12-31 01:08:26gregory.p.smithsetnosy: + gregory.p.smith
messages: + msg332779
2018-10-20 00:04:29Ben.Darnellsetnosy: + Ben.Darnell
messages: + msg328093
2018-01-22 20:19:49xgdomingosetnosy: + xgdomingo
2017-08-05 20:07:04nikratiosetmessages: + msg299793
2017-08-05 01:49:39martin.pantersetnosy: + martin.panter
messages: + msg299766
2017-08-04 19:16:41nikratiocreate