bpo-32533: Fixed thread-safety of error handling in _ssl. (GH-7158)

This commit is contained in:
Steve Dower 2018-09-17 11:34:47 -07:00 committed by GitHub
parent 12a69db908
commit c6fd1c1c3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 60 deletions

View File

@ -0,0 +1 @@
Fixed thread-safety of error handling in _ssl.

View File

@ -423,6 +423,14 @@ typedef struct {
int protocol; int protocol;
} PySSLContext; } PySSLContext;
typedef struct {
int ssl; /* last seen error from SSL */
int c; /* last seen error from libc */
#ifdef MS_WINDOWS
int ws; /* last seen error from winsock */
#endif
} _PySSLError;
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
PyObject *Socket; /* weakref to socket on which we're layered */ PyObject *Socket; /* weakref to socket on which we're layered */
@ -432,11 +440,7 @@ typedef struct {
enum py_ssl_server_or_client socket_type; enum py_ssl_server_or_client socket_type;
PyObject *owner; /* Python level "owner" passed to servername callback */ PyObject *owner; /* Python level "owner" passed to servername callback */
PyObject *server_hostname; PyObject *server_hostname;
int ssl_errno; /* last seen error from SSL */ _PySSLError err; /* last seen error from various sources */
int c_errno; /* last seen error from libc */
#ifdef MS_WINDOWS
int ws_errno; /* last seen error from winsock */
#endif
} PySSLSocket; } PySSLSocket;
typedef struct { typedef struct {
@ -456,20 +460,19 @@ static PyTypeObject PySSLSocket_Type;
static PyTypeObject PySSLMemoryBIO_Type; static PyTypeObject PySSLMemoryBIO_Type;
static PyTypeObject PySSLSession_Type; static PyTypeObject PySSLSession_Type;
static inline _PySSLError _PySSL_errno(int failed, const SSL *ssl, int retcode)
{
_PySSLError err = { 0 };
if (failed) {
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
#define _PySSL_UPDATE_ERRNO_IF(cond, sock, retcode) if (cond) { \ err.ws = WSAGetLastError();
(sock)->ws_errno = WSAGetLastError(); \ _PySSL_FIX_ERRNO;
_PySSL_FIX_ERRNO; \
(sock)->c_errno = errno; \
(sock)->ssl_errno = SSL_get_error((sock->ssl), (retcode)); \
} else { sock->ws_errno = 0; sock->c_errno = 0; sock->ssl_errno = 0; }
#else
#define _PySSL_UPDATE_ERRNO_IF(cond, sock, retcode) if (cond) { \
(sock)->c_errno = errno; \
(sock)->ssl_errno = SSL_get_error((sock->ssl), (retcode)); \
} else { (sock)->c_errno = 0; (sock)->ssl_errno = 0; }
#endif #endif
#define _PySSL_UPDATE_ERRNO(sock, retcode) _PySSL_UPDATE_ERRNO_IF(1, (sock), (retcode)) err.c = errno;
err.ssl = SSL_get_error(ssl, retcode);
}
return err;
}
/*[clinic input] /*[clinic input]
module _ssl module _ssl
@ -703,7 +706,7 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno)
{ {
PyObject *type = PySSLErrorObject; PyObject *type = PySSLErrorObject;
char *errstr = NULL; char *errstr = NULL;
int err; _PySSLError err;
enum py_ssl_error p = PY_SSL_ERROR_NONE; enum py_ssl_error p = PY_SSL_ERROR_NONE;
unsigned long e = 0; unsigned long e = 0;
@ -711,9 +714,9 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno)
e = ERR_peek_last_error(); e = ERR_peek_last_error();
if (sslsock->ssl != NULL) { if (sslsock->ssl != NULL) {
err = sslsock->ssl_errno; err = sslsock->err;
switch (err) { switch (err.ssl) {
case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_ZERO_RETURN:
errstr = "TLS/SSL connection has been closed (EOF)"; errstr = "TLS/SSL connection has been closed (EOF)";
type = PySSLZeroReturnErrorObject; type = PySSLZeroReturnErrorObject;
@ -749,11 +752,12 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno)
/* underlying BIO reported an I/O error */ /* underlying BIO reported an I/O error */
ERR_clear_error(); ERR_clear_error();
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
if (sslsock->ws_errno) if (err.ws) {
return PyErr_SetFromWindowsErr(sslsock->ws_errno); return PyErr_SetFromWindowsErr(err.ws);
}
#endif #endif
if (sslsock->c_errno) { if (err.c) {
errno = sslsock->c_errno; errno = err.c;
return PyErr_SetFromErrno(PyExc_OSError); return PyErr_SetFromErrno(PyExc_OSError);
} }
Py_INCREF(s); Py_INCREF(s);
@ -883,6 +887,7 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
{ {
PySSLSocket *self; PySSLSocket *self;
SSL_CTX *ctx = sslctx->ctx; SSL_CTX *ctx = sslctx->ctx;
_PySSLError err = { 0 };
self = PyObject_New(PySSLSocket, &PySSLSocket_Type); self = PyObject_New(PySSLSocket, &PySSLSocket_Type);
if (self == NULL) if (self == NULL)
@ -895,11 +900,7 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
self->shutdown_seen_zero = 0; self->shutdown_seen_zero = 0;
self->owner = NULL; self->owner = NULL;
self->server_hostname = NULL; self->server_hostname = NULL;
self->ssl_errno = 0; self->err = err;
self->c_errno = 0;
#ifdef MS_WINDOWS
self->ws_errno = 0;
#endif
/* Make sure the SSL error state is initialized */ /* Make sure the SSL error state is initialized */
ERR_clear_error(); ERR_clear_error();
@ -976,7 +977,7 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
/*[clinic end generated code: output=6c0898a8936548f6 input=d2d737de3df018c8]*/ /*[clinic end generated code: output=6c0898a8936548f6 input=d2d737de3df018c8]*/
{ {
int ret; int ret;
int err; _PySSLError err;
int sockstate, nonblocking; int sockstate, nonblocking;
PySocketSockObject *sock = GET_SOCKET(self); PySocketSockObject *sock = GET_SOCKET(self);
_PyTime_t timeout, deadline = 0; _PyTime_t timeout, deadline = 0;
@ -1006,9 +1007,9 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
do { do {
PySSL_BEGIN_ALLOW_THREADS PySSL_BEGIN_ALLOW_THREADS
ret = SSL_do_handshake(self->ssl); ret = SSL_do_handshake(self->ssl);
_PySSL_UPDATE_ERRNO_IF(ret < 1, self, ret); err = _PySSL_errno(ret < 1, self->ssl, ret);
PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS
err = self->ssl_errno; self->err = err;
if (PyErr_CheckSignals()) if (PyErr_CheckSignals())
goto error; goto error;
@ -1016,9 +1017,9 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
if (has_timeout) if (has_timeout)
timeout = deadline - _PyTime_GetMonotonicClock(); timeout = deadline - _PyTime_GetMonotonicClock();
if (err == SSL_ERROR_WANT_READ) { if (err.ssl == SSL_ERROR_WANT_READ) {
sockstate = PySSL_select(sock, 0, timeout); sockstate = PySSL_select(sock, 0, timeout);
} else if (err == SSL_ERROR_WANT_WRITE) { } else if (err.ssl == SSL_ERROR_WANT_WRITE) {
sockstate = PySSL_select(sock, 1, timeout); sockstate = PySSL_select(sock, 1, timeout);
} else { } else {
sockstate = SOCKET_OPERATION_OK; sockstate = SOCKET_OPERATION_OK;
@ -1039,7 +1040,8 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
} else if (sockstate == SOCKET_IS_NONBLOCKING) { } else if (sockstate == SOCKET_IS_NONBLOCKING) {
break; break;
} }
} while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); } while (err.ssl == SSL_ERROR_WANT_READ ||
err.ssl == SSL_ERROR_WANT_WRITE);
Py_XDECREF(sock); Py_XDECREF(sock);
if (ret < 1) if (ret < 1)
return PySSL_SetError(self, ret, __FILE__, __LINE__); return PySSL_SetError(self, ret, __FILE__, __LINE__);
@ -2228,7 +2230,7 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
{ {
int len; int len;
int sockstate; int sockstate;
int err; _PySSLError err;
int nonblocking; int nonblocking;
PySocketSockObject *sock = GET_SOCKET(self); PySocketSockObject *sock = GET_SOCKET(self);
_PyTime_t timeout, deadline = 0; _PyTime_t timeout, deadline = 0;
@ -2279,9 +2281,9 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
do { do {
PySSL_BEGIN_ALLOW_THREADS PySSL_BEGIN_ALLOW_THREADS
len = SSL_write(self->ssl, b->buf, (int)b->len); len = SSL_write(self->ssl, b->buf, (int)b->len);
_PySSL_UPDATE_ERRNO_IF(len <= 0, self, len); err = _PySSL_errno(len <= 0, self->ssl, len);
PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS
err = self->ssl_errno; self->err = err;
if (PyErr_CheckSignals()) if (PyErr_CheckSignals())
goto error; goto error;
@ -2289,9 +2291,9 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
if (has_timeout) if (has_timeout)
timeout = deadline - _PyTime_GetMonotonicClock(); timeout = deadline - _PyTime_GetMonotonicClock();
if (err == SSL_ERROR_WANT_READ) { if (err.ssl == SSL_ERROR_WANT_READ) {
sockstate = PySSL_select(sock, 0, timeout); sockstate = PySSL_select(sock, 0, timeout);
} else if (err == SSL_ERROR_WANT_WRITE) { } else if (err.ssl == SSL_ERROR_WANT_WRITE) {
sockstate = PySSL_select(sock, 1, timeout); sockstate = PySSL_select(sock, 1, timeout);
} else { } else {
sockstate = SOCKET_OPERATION_OK; sockstate = SOCKET_OPERATION_OK;
@ -2308,7 +2310,8 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
} else if (sockstate == SOCKET_IS_NONBLOCKING) { } else if (sockstate == SOCKET_IS_NONBLOCKING) {
break; break;
} }
} while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); } while (err.ssl == SSL_ERROR_WANT_READ ||
err.ssl == SSL_ERROR_WANT_WRITE);
Py_XDECREF(sock); Py_XDECREF(sock);
if (len > 0) if (len > 0)
@ -2332,11 +2335,14 @@ _ssl__SSLSocket_pending_impl(PySSLSocket *self)
/*[clinic end generated code: output=983d9fecdc308a83 input=2b77487d6dfd597f]*/ /*[clinic end generated code: output=983d9fecdc308a83 input=2b77487d6dfd597f]*/
{ {
int count = 0; int count = 0;
_PySSLError err;
PySSL_BEGIN_ALLOW_THREADS PySSL_BEGIN_ALLOW_THREADS
count = SSL_pending(self->ssl); count = SSL_pending(self->ssl);
_PySSL_UPDATE_ERRNO_IF(count < 0, self, count); err = _PySSL_errno(count < 0, self->ssl, count);
PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS
self->err = err;
if (count < 0) if (count < 0)
return PySSL_SetError(self, count, __FILE__, __LINE__); return PySSL_SetError(self, count, __FILE__, __LINE__);
else else
@ -2363,7 +2369,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
char *mem; char *mem;
int count; int count;
int sockstate; int sockstate;
int err; _PySSLError err;
int nonblocking; int nonblocking;
PySocketSockObject *sock = GET_SOCKET(self); PySocketSockObject *sock = GET_SOCKET(self);
_PyTime_t timeout, deadline = 0; _PyTime_t timeout, deadline = 0;
@ -2424,8 +2430,9 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
do { do {
PySSL_BEGIN_ALLOW_THREADS PySSL_BEGIN_ALLOW_THREADS
count = SSL_read(self->ssl, mem, len); count = SSL_read(self->ssl, mem, len);
_PySSL_UPDATE_ERRNO_IF(count <= 0, self, count); err = _PySSL_errno(count <= 0, self->ssl, count);
PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS
self->err = err;
if (PyErr_CheckSignals()) if (PyErr_CheckSignals())
goto error; goto error;
@ -2433,12 +2440,11 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
if (has_timeout) if (has_timeout)
timeout = deadline - _PyTime_GetMonotonicClock(); timeout = deadline - _PyTime_GetMonotonicClock();
err = self->ssl_errno; if (err.ssl == SSL_ERROR_WANT_READ) {
if (err == SSL_ERROR_WANT_READ) {
sockstate = PySSL_select(sock, 0, timeout); sockstate = PySSL_select(sock, 0, timeout);
} else if (err == SSL_ERROR_WANT_WRITE) { } else if (err.ssl == SSL_ERROR_WANT_WRITE) {
sockstate = PySSL_select(sock, 1, timeout); sockstate = PySSL_select(sock, 1, timeout);
} else if (err == SSL_ERROR_ZERO_RETURN && } else if (err.ssl == SSL_ERROR_ZERO_RETURN &&
SSL_get_shutdown(self->ssl) == SSL_RECEIVED_SHUTDOWN) SSL_get_shutdown(self->ssl) == SSL_RECEIVED_SHUTDOWN)
{ {
count = 0; count = 0;
@ -2454,7 +2460,8 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
} else if (sockstate == SOCKET_IS_NONBLOCKING) { } else if (sockstate == SOCKET_IS_NONBLOCKING) {
break; break;
} }
} while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); } while (err.ssl == SSL_ERROR_WANT_READ ||
err.ssl == SSL_ERROR_WANT_WRITE);
if (count <= 0) { if (count <= 0) {
PySSL_SetError(self, count, __FILE__, __LINE__); PySSL_SetError(self, count, __FILE__, __LINE__);
@ -2488,7 +2495,8 @@ static PyObject *
_ssl__SSLSocket_shutdown_impl(PySSLSocket *self) _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
/*[clinic end generated code: output=ca1aa7ed9d25ca42 input=11d39e69b0a2bf4a]*/ /*[clinic end generated code: output=ca1aa7ed9d25ca42 input=11d39e69b0a2bf4a]*/
{ {
int err, sockstate, nonblocking; _PySSLError err;
int sockstate, nonblocking, ret;
int zeros = 0; int zeros = 0;
PySocketSockObject *sock = GET_SOCKET(self); PySocketSockObject *sock = GET_SOCKET(self);
_PyTime_t timeout, deadline = 0; _PyTime_t timeout, deadline = 0;
@ -2526,14 +2534,15 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
*/ */
if (self->shutdown_seen_zero) if (self->shutdown_seen_zero)
SSL_set_read_ahead(self->ssl, 0); SSL_set_read_ahead(self->ssl, 0);
err = SSL_shutdown(self->ssl); ret = SSL_shutdown(self->ssl);
_PySSL_UPDATE_ERRNO_IF(err < 0, self, err); err = _PySSL_errno(ret < 0, self->ssl, ret);
PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS
self->err = err;
/* If err == 1, a secure shutdown with SSL_shutdown() is complete */ /* If err == 1, a secure shutdown with SSL_shutdown() is complete */
if (err > 0) if (ret > 0)
break; break;
if (err == 0) { if (ret == 0) {
/* Don't loop endlessly; instead preserve legacy /* Don't loop endlessly; instead preserve legacy
behaviour of trying SSL_shutdown() only twice. behaviour of trying SSL_shutdown() only twice.
This looks necessary for OpenSSL < 0.9.8m */ This looks necessary for OpenSSL < 0.9.8m */
@ -2548,16 +2557,15 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
timeout = deadline - _PyTime_GetMonotonicClock(); timeout = deadline - _PyTime_GetMonotonicClock();
/* Possibly retry shutdown until timeout or failure */ /* Possibly retry shutdown until timeout or failure */
_PySSL_UPDATE_ERRNO(self, err); if (err.ssl == SSL_ERROR_WANT_READ)
if (self->ssl_errno == SSL_ERROR_WANT_READ)
sockstate = PySSL_select(sock, 0, timeout); sockstate = PySSL_select(sock, 0, timeout);
else if (self->ssl_errno == SSL_ERROR_WANT_WRITE) else if (err.ssl == SSL_ERROR_WANT_WRITE)
sockstate = PySSL_select(sock, 1, timeout); sockstate = PySSL_select(sock, 1, timeout);
else else
break; break;
if (sockstate == SOCKET_HAS_TIMED_OUT) { if (sockstate == SOCKET_HAS_TIMED_OUT) {
if (self->ssl_errno == SSL_ERROR_WANT_READ) if (err.ssl == SSL_ERROR_WANT_READ)
PyErr_SetString(PySocketModule.timeout_error, PyErr_SetString(PySocketModule.timeout_error,
"The read operation timed out"); "The read operation timed out");
else else
@ -2575,9 +2583,9 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
break; break;
} }
if (err < 0) { if (err.ssl < 0) {
Py_XDECREF(sock); Py_XDECREF(sock);
return PySSL_SetError(self, err, __FILE__, __LINE__); return PySSL_SetError(self, err.ssl, __FILE__, __LINE__);
} }
if (sock) if (sock)
/* It's already INCREF'ed */ /* It's already INCREF'ed */