Skip to content

Commit ea9a296

Browse files
keepworkingserhiy-storchakaencukou
authored
pythongh-115627: Fix PySSL_SetError handling SSL_ERROR_SYSCALL (pythonGH-115628)
Python 3.10 changed from using SSL_write() and SSL_read() to SSL_write_ex() and SSL_read_ex(), but did not update handling of the return value. Change error handling so that the return value is not examined. OSError (not EOF) is now returned when retval is 0. According to *recent* man pages of all functions for which we call PySSL_SetError, (in OpenSSL 3.0 and 1.1.1), their return value should be used to determine whether an error happened (i.e. if PySSL_SetError should be called), but not what kind of error happened (so, PySSL_SetError shouldn't need retval). To get the error, we need to use SSL_get_error. Co-authored-by: Serhiy Storchaka <[email protected]> Co-authored-by: Petr Viktorin <[email protected]>
1 parent d52bdfb commit ea9a296

File tree

3 files changed

+35
-43
lines changed

3 files changed

+35
-43
lines changed

Lib/test/test_ssl.py

+15-13
Original file line numberDiff line numberDiff line change
@@ -2429,16 +2429,18 @@ def run(self):
24292429
self.write(msg.lower())
24302430
except OSError as e:
24312431
# handles SSLError and socket errors
2432+
if isinstance(e, ConnectionError):
2433+
# OpenSSL 1.1.1 sometimes raises
2434+
# ConnectionResetError when connection is not
2435+
# shut down gracefully.
2436+
if self.server.chatty and support.verbose:
2437+
print(f" Connection reset by peer: {self.addr}")
2438+
2439+
self.close()
2440+
self.running = False
2441+
return
24322442
if self.server.chatty and support.verbose:
2433-
if isinstance(e, ConnectionError):
2434-
# OpenSSL 1.1.1 sometimes raises
2435-
# ConnectionResetError when connection is not
2436-
# shut down gracefully.
2437-
print(
2438-
f" Connection reset by peer: {self.addr}"
2439-
)
2440-
else:
2441-
handle_error("Test server failure:\n")
2443+
handle_error("Test server failure:\n")
24422444
try:
24432445
self.write(b"ERROR\n")
24442446
except OSError:
@@ -3166,8 +3168,8 @@ def test_wrong_cert_tls13(self):
31663168
suppress_ragged_eofs=False) as s:
31673169
s.connect((HOST, server.port))
31683170
with self.assertRaisesRegex(
3169-
ssl.SSLError,
3170-
'alert unknown ca|EOF occurred|TLSV1_ALERT_UNKNOWN_CA'
3171+
OSError,
3172+
'alert unknown ca|EOF occurred|TLSV1_ALERT_UNKNOWN_CA|closed by the remote host|Connection reset by peer'
31713173
):
31723174
# TLS 1.3 perform client cert exchange after handshake
31733175
s.write(b'data')
@@ -4532,8 +4534,8 @@ def msg_cb(conn, direction, version, content_type, msg_type, data):
45324534
# test sometimes fails with EOF error. Test passes as long as
45334535
# server aborts connection with an error.
45344536
with self.assertRaisesRegex(
4535-
ssl.SSLError,
4536-
'(certificate required|EOF occurred)'
4537+
OSError,
4538+
'certificate required|EOF occurred|closed by the remote host|Connection reset by peer'
45374539
):
45384540
# receive CertificateRequest
45394541
data = s.recv(1024)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix the :mod:`ssl` module error handling of connection terminate by peer.
2+
It now throws an OSError with the appropriate error code instead of an EOFError.

Modules/_ssl.c

+18-30
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ PySSL_ChainExceptions(PySSLSocket *sslsock) {
599599
}
600600

601601
static PyObject *
602-
PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno)
602+
PySSL_SetError(PySSLSocket *sslsock, const char *filename, int lineno)
603603
{
604604
PyObject *type;
605605
char *errstr = NULL;
@@ -612,7 +612,6 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno)
612612
_sslmodulestate *state = get_state_sock(sslsock);
613613
type = state->PySSLErrorObject;
614614

615-
assert(ret <= 0);
616615
e = ERR_peek_last_error();
617616

618617
if (sslsock->ssl != NULL) {
@@ -645,32 +644,21 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno)
645644
case SSL_ERROR_SYSCALL:
646645
{
647646
if (e == 0) {
648-
PySocketSockObject *s = GET_SOCKET(sslsock);
649-
if (ret == 0 || (((PyObject *)s) == Py_None)) {
647+
/* underlying BIO reported an I/O error */
648+
ERR_clear_error();
649+
#ifdef MS_WINDOWS
650+
if (err.ws) {
651+
return PyErr_SetFromWindowsErr(err.ws);
652+
}
653+
#endif
654+
if (err.c) {
655+
errno = err.c;
656+
return PyErr_SetFromErrno(PyExc_OSError);
657+
}
658+
else {
650659
p = PY_SSL_ERROR_EOF;
651660
type = state->PySSLEOFErrorObject;
652661
errstr = "EOF occurred in violation of protocol";
653-
} else if (s && ret == -1) {
654-
/* underlying BIO reported an I/O error */
655-
ERR_clear_error();
656-
#ifdef MS_WINDOWS
657-
if (err.ws) {
658-
return PyErr_SetFromWindowsErr(err.ws);
659-
}
660-
#endif
661-
if (err.c) {
662-
errno = err.c;
663-
return PyErr_SetFromErrno(PyExc_OSError);
664-
}
665-
else {
666-
p = PY_SSL_ERROR_EOF;
667-
type = state->PySSLEOFErrorObject;
668-
errstr = "EOF occurred in violation of protocol";
669-
}
670-
} else { /* possible? */
671-
p = PY_SSL_ERROR_SYSCALL;
672-
type = state->PySSLSyscallErrorObject;
673-
errstr = "Some I/O error occurred";
674662
}
675663
} else {
676664
if (ERR_GET_LIB(e) == ERR_LIB_SSL &&
@@ -1030,7 +1018,7 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
10301018
err.ssl == SSL_ERROR_WANT_WRITE);
10311019
Py_XDECREF(sock);
10321020
if (ret < 1)
1033-
return PySSL_SetError(self, ret, __FILE__, __LINE__);
1021+
return PySSL_SetError(self, __FILE__, __LINE__);
10341022
if (PySSL_ChainExceptions(self) < 0)
10351023
return NULL;
10361024
Py_RETURN_NONE;
@@ -2437,7 +2425,7 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
24372425

24382426
Py_XDECREF(sock);
24392427
if (retval == 0)
2440-
return PySSL_SetError(self, retval, __FILE__, __LINE__);
2428+
return PySSL_SetError(self, __FILE__, __LINE__);
24412429
if (PySSL_ChainExceptions(self) < 0)
24422430
return NULL;
24432431
return PyLong_FromSize_t(count);
@@ -2467,7 +2455,7 @@ _ssl__SSLSocket_pending_impl(PySSLSocket *self)
24672455
self->err = err;
24682456

24692457
if (count < 0)
2470-
return PySSL_SetError(self, count, __FILE__, __LINE__);
2458+
return PySSL_SetError(self, __FILE__, __LINE__);
24712459
else
24722460
return PyLong_FromLong(count);
24732461
}
@@ -2590,7 +2578,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len,
25902578
err.ssl == SSL_ERROR_WANT_WRITE);
25912579

25922580
if (retval == 0) {
2593-
PySSL_SetError(self, retval, __FILE__, __LINE__);
2581+
PySSL_SetError(self, __FILE__, __LINE__);
25942582
goto error;
25952583
}
25962584
if (self->exc != NULL)
@@ -2716,7 +2704,7 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
27162704
}
27172705
if (ret < 0) {
27182706
Py_XDECREF(sock);
2719-
PySSL_SetError(self, ret, __FILE__, __LINE__);
2707+
PySSL_SetError(self, __FILE__, __LINE__);
27202708
return NULL;
27212709
}
27222710
if (self->exc != NULL)

0 commit comments

Comments
 (0)