Issue 33674: asyncio: race condition in SSLProtocol (original) (raw)

Created on 2018-05-28 20:37 by vstinner, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 7175 merged vstinner,2018-05-28 20:51
PR 7187 merged miss-islington,2018-05-28 23:34
PR 7188 merged vstinner,2018-05-28 23:51
PR 7192 merged yselivanov,2018-05-29 04:39
PR 7193 merged miss-islington,2018-05-29 05:01
Messages (15)
msg317916 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2018-05-28 20:37
While debugging bpo-32458 (functional test on START TLS), I found a race condition in SSLProtocol of asyncio/sslproto.py. Sometimes, _sslpipe.feed_ssldata() is called before _sslpipe.shutdown(). * SSLProtocol.connection_made() -> SSLProtocol._start_handshake(): self._loop.call_soon(self._process_write_backlog) * SSLProtoco.data_received(): direct call to self._sslpipe.feed_ssldata(data) * Later, self._process_write_backlog() calls self._sslpipe.do_handshake() The first write is delayed by call_soon(), whereas the first read is a direct call to the SSL pipe.
msg317917 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2018-05-28 20:38
Workaround: diff --git a/Lib/asyncio/sslproto.py b/Lib/asyncio/sslproto.py index 2bfa45dd15..4a5dbb38a1 100644 --- a/Lib/asyncio/sslproto.py +++ b/Lib/asyncio/sslproto.py @@ -592,7 +592,7 @@ class SSLProtocol(protocols.Protocol): # (b'', 1) is a special value in _process_write_backlog() to do # the SSL handshake self._write_backlog.append((b'', 1)) - self._loop.call_soon(self._process_write_backlog) + self._process_write_backlog() self._handshake_timeout_handle = \ self._loop.call_later(self._ssl_handshake_timeout, self._check_handshake_timeout)
msg317919 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2018-05-28 20:43
loop.start_tls() method is new in Python 3.7. If possible, I would prefer to not see it with a builtin race condition, since such race condition is really hard to debug :-( So I raise the priority to release blocker. Sorry again Ned!
msg317922 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2018-05-28 20:59
Yury Selivanov told me that usually it's safer to add call_soon(), than to remove call_soon(). But adding many call_soon() can make asyncio SSL slower. SSLProtocol doesn't seem to like call_soon(), it's only used at: * connection_lost(): call_soon(self._app_protocol.connection_lost, exc) * connection_made() ~~> call_soon(self._process_write_backlog) * _on_handshake_complete(): call_soon(self._process_write_backlog) That's all. All other operations are done immediately. _on_handshake_complete() contains a long comment: # In case transport.write() was already called. Don't call # immediately _process_write_backlog(), but schedule it: # _on_handshake_complete() can be called indirectly from # _process_write_backlog(), and _process_write_backlog() is not # reentrant. self._loop.call_soon(self._process_write_backlog)
msg317923 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2018-05-28 21:05
The fix is correct and the bug is now obvious: data_received() occur pretty much any time after connection_made() call; if call_soon() is used in connection_made(), data_received() may find the protocol in an incorrect state. Kudos Victor for debugging this.
msg317924 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2018-05-28 21:05
And I agree, this should make it to 3.7.0
msg317960 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2018-05-28 23:33
New changeset be00a5583a2cb696335c527b921d1868266a42c6 by Victor Stinner in branch 'master': bpo-33674: asyncio: Fix SSLProtocol race (GH-7175) https://github.com/python/cpython/commit/be00a5583a2cb696335c527b921d1868266a42c6
msg317974 - (view) Author: Ned Deily (ned.deily) * (Python committer) Date: 2018-05-29 01:44
New changeset 0dd8fd03584b61cd769be88f5a2fb59b0d8f6d18 by Ned Deily (Miss Islington (bot)) in branch '3.7': bpo-33674: asyncio: Fix SSLProtocol race (GH-7175) (#7187) https://github.com/python/cpython/commit/0dd8fd03584b61cd769be88f5a2fb59b0d8f6d18
msg317981 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2018-05-29 04:46
New changeset 7593b8a5075ff45d71be9f62980be6a9c005afa9 by Yury Selivanov (Victor Stinner) in branch '3.6': bpo-33674: asyncio: Fix SSLProtocol race (GH-7175) (GH-7188) https://github.com/python/cpython/commit/7593b8a5075ff45d71be9f62980be6a9c005afa9
msg317983 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2018-05-29 05:00
New changeset f295587c45f96b62d24f9a12cef6931b0805f596 by Yury Selivanov in branch 'master': bpo-33674: Pause the transport as early as possible (#7192) https://github.com/python/cpython/commit/f295587c45f96b62d24f9a12cef6931b0805f596
msg317984 - (view) Author: miss-islington (miss-islington) Date: 2018-05-29 05:59
New changeset eca085993cb8620ba9232560f46d91326a13cdd2 by Miss Islington (bot) in branch '3.7': bpo-33674: Pause the transport as early as possible (GH-7192) https://github.com/python/cpython/commit/eca085993cb8620ba9232560f46d91326a13cdd2
msg318003 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2018-05-29 10:16
I fixed the main issue, so I remove the "release blocker" priority.
msg318004 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2018-05-29 10:28
> bpo-33674: Pause the transport as early as possible (#7192) > https://github.com/python/cpython/commit/f295587c45f96b62d24f9a12cef6931b0805f596 Is it ok to always resume reading? Previously reading was only resumed if the transport was reading.
msg318025 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2018-05-29 13:39
yes, the method is idempotent.
msg318044 - (view) Author: Yury Selivanov (yselivanov) * (Python committer) Date: 2018-05-29 15:17
Closing this one. Please open new issues to track regressions.
History
Date User Action Args
2022-04-11 14:59:00 admin set github: 77855
2018-05-29 15:17:16 yselivanov set status: open -> closedresolution: fixedmessages: + stage: patch review -> resolved
2018-05-29 13:39:36 yselivanov set messages: +
2018-05-29 10:28:08 vstinner set messages: +
2018-05-29 10:16:40 vstinner set priority: release blocker -> messages: +
2018-05-29 05:59:06 miss-islington set nosy: + miss-islingtonmessages: +
2018-05-29 05:01:30 miss-islington set pull_requests: + <pull%5Frequest6827>
2018-05-29 05:00:14 yselivanov set messages: +
2018-05-29 04:46:53 yselivanov set messages: +
2018-05-29 04:39:01 yselivanov set pull_requests: + <pull%5Frequest6826>
2018-05-29 01:44:18 ned.deily set messages: +
2018-05-28 23:51:25 vstinner set pull_requests: + <pull%5Frequest6823>
2018-05-28 23:34:45 miss-islington set pull_requests: + <pull%5Frequest6822>
2018-05-28 23:33:37 vstinner set messages: +
2018-05-28 21:05:29 yselivanov set messages: +
2018-05-28 21:05:15 yselivanov set messages: +
2018-05-28 20:59:47 vstinner set messages: +
2018-05-28 20:51:06 vstinner set keywords: + patchstage: patch reviewpull_requests: + <pull%5Frequest6810>
2018-05-28 20:43:58 vstinner set priority: normal -> release blockernosy: + ned.deilymessages: +
2018-05-28 20:38:02 vstinner set messages: +
2018-05-28 20:37:18 vstinner create