gh-134698: Hold a lock when the thread state is detached in ssl (GH-134724)

Lock when the thread state is detached.

Co-authored-by: Gregory P. Smith <greg@krypto.org>
This commit is contained in:
Peter Bierma 2025-07-25 10:16:05 -05:00 committed by GitHub
parent cb93b6fc5e
commit e047a35b23
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 89 additions and 50 deletions

View file

@ -1239,6 +1239,25 @@ def getpass(self):
# Make sure the password function isn't called if it isn't needed # Make sure the password function isn't called if it isn't needed
ctx.load_cert_chain(CERTFILE, password=getpass_exception) ctx.load_cert_chain(CERTFILE, password=getpass_exception)
@threading_helper.requires_working_threading()
def test_load_cert_chain_thread_safety(self):
# gh-134698: _ssl detaches the thread state (and as such,
# releases the GIL and critical sections) around expensive
# OpenSSL calls. Unfortunately, OpenSSL structures aren't
# thread-safe, so executing these calls concurrently led
# to crashes.
ctx = ssl.create_default_context()
def race():
ctx.load_cert_chain(CERTFILE)
threads = [threading.Thread(target=race) for _ in range(8)]
with threading_helper.catch_threading_exception() as cm:
with threading_helper.start_threads(threads):
pass
self.assertIsNone(cm.exc_value)
def test_load_verify_locations(self): def test_load_verify_locations(self):
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_verify_locations(CERTFILE) ctx.load_verify_locations(CERTFILE)

View file

@ -0,0 +1,2 @@
Fix a crash when calling methods of :class:`ssl.SSLContext` or
:class:`ssl.SSLSocket` across multiple threads.

View file

@ -43,14 +43,14 @@
/* Redefined below for Windows debug builds after important #includes */ /* Redefined below for Windows debug builds after important #includes */
#define _PySSL_FIX_ERRNO #define _PySSL_FIX_ERRNO
#define PySSL_BEGIN_ALLOW_THREADS_S(save) \ #define PySSL_BEGIN_ALLOW_THREADS_S(save, mutex) \
do { (save) = PyEval_SaveThread(); } while(0) do { (save) = PyEval_SaveThread(); PyMutex_Lock(mutex); } while(0)
#define PySSL_END_ALLOW_THREADS_S(save) \ #define PySSL_END_ALLOW_THREADS_S(save, mutex) \
do { PyEval_RestoreThread(save); _PySSL_FIX_ERRNO; } while(0) do { PyMutex_Unlock(mutex); PyEval_RestoreThread(save); _PySSL_FIX_ERRNO; } while(0)
#define PySSL_BEGIN_ALLOW_THREADS { \ #define PySSL_BEGIN_ALLOW_THREADS(self) { \
PyThreadState *_save = NULL; \ PyThreadState *_save = NULL; \
PySSL_BEGIN_ALLOW_THREADS_S(_save); PySSL_BEGIN_ALLOW_THREADS_S(_save, &self->tstate_mutex);
#define PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS_S(_save); } #define PySSL_END_ALLOW_THREADS(self) PySSL_END_ALLOW_THREADS_S(_save, &self->tstate_mutex); }
#if defined(HAVE_POLL_H) #if defined(HAVE_POLL_H)
#include <poll.h> #include <poll.h>
@ -336,6 +336,9 @@ typedef struct {
PyObject *psk_client_callback; PyObject *psk_client_callback;
PyObject *psk_server_callback; PyObject *psk_server_callback;
#endif #endif
/* Lock to synchronize calls when the thread state is detached.
See also gh-134698. */
PyMutex tstate_mutex;
} PySSLContext; } PySSLContext;
#define PySSLContext_CAST(op) ((PySSLContext *)(op)) #define PySSLContext_CAST(op) ((PySSLContext *)(op))
@ -363,6 +366,9 @@ typedef struct {
* and shutdown methods check for chained exceptions. * and shutdown methods check for chained exceptions.
*/ */
PyObject *exc; PyObject *exc;
/* Lock to synchronize calls when the thread state is detached.
See also gh-134698. */
PyMutex tstate_mutex;
} PySSLSocket; } PySSLSocket;
#define PySSLSocket_CAST(op) ((PySSLSocket *)(op)) #define PySSLSocket_CAST(op) ((PySSLSocket *)(op))
@ -912,13 +918,14 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
self->server_hostname = NULL; self->server_hostname = NULL;
self->err = err; self->err = err;
self->exc = NULL; self->exc = NULL;
self->tstate_mutex = (PyMutex){0};
/* Make sure the SSL error state is initialized */ /* Make sure the SSL error state is initialized */
ERR_clear_error(); ERR_clear_error();
PySSL_BEGIN_ALLOW_THREADS PySSL_BEGIN_ALLOW_THREADS(sslctx)
self->ssl = SSL_new(ctx); self->ssl = SSL_new(ctx);
PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS(sslctx)
if (self->ssl == NULL) { if (self->ssl == NULL) {
Py_DECREF(self); Py_DECREF(self);
_setSSLError(get_state_ctx(self), NULL, 0, __FILE__, __LINE__); _setSSLError(get_state_ctx(self), NULL, 0, __FILE__, __LINE__);
@ -987,12 +994,12 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
BIO_set_nbio(SSL_get_wbio(self->ssl), 1); BIO_set_nbio(SSL_get_wbio(self->ssl), 1);
} }
PySSL_BEGIN_ALLOW_THREADS PySSL_BEGIN_ALLOW_THREADS(self)
if (socket_type == PY_SSL_CLIENT) if (socket_type == PY_SSL_CLIENT)
SSL_set_connect_state(self->ssl); SSL_set_connect_state(self->ssl);
else else
SSL_set_accept_state(self->ssl); SSL_set_accept_state(self->ssl);
PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS(self)
self->socket_type = socket_type; self->socket_type = socket_type;
if (sock != NULL) { if (sock != NULL) {
@ -1061,10 +1068,10 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
/* Actually negotiate SSL connection */ /* Actually negotiate SSL connection */
/* XXX If SSL_do_handshake() returns 0, it's also a failure. */ /* XXX If SSL_do_handshake() returns 0, it's also a failure. */
do { do {
PySSL_BEGIN_ALLOW_THREADS PySSL_BEGIN_ALLOW_THREADS(self)
ret = SSL_do_handshake(self->ssl); ret = SSL_do_handshake(self->ssl);
err = _PySSL_errno(ret < 1, self->ssl, ret); err = _PySSL_errno(ret < 1, self->ssl, ret);
PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS(self)
self->err = err; self->err = err;
if (PyErr_CheckSignals()) if (PyErr_CheckSignals())
@ -2441,9 +2448,10 @@ PySSL_select(PySocketSockObject *s, int writing, PyTime_t timeout)
ms = (int)_PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING); ms = (int)_PyTime_AsMilliseconds(timeout, _PyTime_ROUND_CEILING);
assert(ms <= INT_MAX); assert(ms <= INT_MAX);
PySSL_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
rc = poll(&pollfd, 1, (int)ms); rc = poll(&pollfd, 1, (int)ms);
PySSL_END_ALLOW_THREADS Py_END_ALLOW_THREADS
_PySSL_FIX_ERRNO;
#else #else
/* Guard against socket too large for select*/ /* Guard against socket too large for select*/
if (!_PyIsSelectable_fd(s->sock_fd)) if (!_PyIsSelectable_fd(s->sock_fd))
@ -2455,13 +2463,14 @@ PySSL_select(PySocketSockObject *s, int writing, PyTime_t timeout)
FD_SET(s->sock_fd, &fds); FD_SET(s->sock_fd, &fds);
/* Wait until the socket becomes ready */ /* Wait until the socket becomes ready */
PySSL_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
nfds = Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int); nfds = Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int);
if (writing) if (writing)
rc = select(nfds, NULL, &fds, NULL, &tv); rc = select(nfds, NULL, &fds, NULL, &tv);
else else
rc = select(nfds, &fds, NULL, NULL, &tv); rc = select(nfds, &fds, NULL, NULL, &tv);
PySSL_END_ALLOW_THREADS Py_END_ALLOW_THREADS
_PySSL_FIX_ERRNO;
#endif #endif
/* Return SOCKET_TIMED_OUT on timeout, SOCKET_OPERATION_OK otherwise /* Return SOCKET_TIMED_OUT on timeout, SOCKET_OPERATION_OK otherwise
@ -2579,10 +2588,10 @@ _ssl__SSLSocket_sendfile_impl(PySSLSocket *self, int fd, Py_off_t offset,
} }
do { do {
PySSL_BEGIN_ALLOW_THREADS PySSL_BEGIN_ALLOW_THREADS(self)
retval = SSL_sendfile(self->ssl, fd, (off_t)offset, size, flags); retval = SSL_sendfile(self->ssl, fd, (off_t)offset, size, flags);
err = _PySSL_errno(retval < 0, self->ssl, (int)retval); err = _PySSL_errno(retval < 0, self->ssl, (int)retval);
PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS(self)
self->err = err; self->err = err;
if (PyErr_CheckSignals()) { if (PyErr_CheckSignals()) {
@ -2710,10 +2719,10 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
} }
do { do {
PySSL_BEGIN_ALLOW_THREADS PySSL_BEGIN_ALLOW_THREADS(self)
retval = SSL_write_ex(self->ssl, b->buf, (size_t)b->len, &count); retval = SSL_write_ex(self->ssl, b->buf, (size_t)b->len, &count);
err = _PySSL_errno(retval == 0, self->ssl, retval); err = _PySSL_errno(retval == 0, self->ssl, retval);
PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS(self)
self->err = err; self->err = err;
if (PyErr_CheckSignals()) if (PyErr_CheckSignals())
@ -2771,10 +2780,10 @@ _ssl__SSLSocket_pending_impl(PySSLSocket *self)
int count = 0; int count = 0;
_PySSLError err; _PySSLError err;
PySSL_BEGIN_ALLOW_THREADS PySSL_BEGIN_ALLOW_THREADS(self)
count = SSL_pending(self->ssl); count = SSL_pending(self->ssl);
err = _PySSL_errno(count < 0, self->ssl, count); err = _PySSL_errno(count < 0, self->ssl, count);
PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS(self)
self->err = err; self->err = err;
if (count < 0) if (count < 0)
@ -2865,10 +2874,10 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, Py_ssize_t len,
deadline = _PyDeadline_Init(timeout); deadline = _PyDeadline_Init(timeout);
do { do {
PySSL_BEGIN_ALLOW_THREADS PySSL_BEGIN_ALLOW_THREADS(self)
retval = SSL_read_ex(self->ssl, mem, (size_t)len, &count); retval = SSL_read_ex(self->ssl, mem, (size_t)len, &count);
err = _PySSL_errno(retval == 0, self->ssl, retval); err = _PySSL_errno(retval == 0, self->ssl, retval);
PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS(self)
self->err = err; self->err = err;
if (PyErr_CheckSignals()) if (PyErr_CheckSignals())
@ -2967,7 +2976,7 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
} }
while (1) { while (1) {
PySSL_BEGIN_ALLOW_THREADS PySSL_BEGIN_ALLOW_THREADS(self)
/* Disable read-ahead so that unwrap can work correctly. /* Disable read-ahead so that unwrap can work correctly.
* Otherwise OpenSSL might read in too much data, * Otherwise OpenSSL might read in too much data,
* eating clear text data that happens to be * eating clear text data that happens to be
@ -2980,7 +2989,7 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
SSL_set_read_ahead(self->ssl, 0); SSL_set_read_ahead(self->ssl, 0);
ret = SSL_shutdown(self->ssl); ret = SSL_shutdown(self->ssl);
err = _PySSL_errno(ret < 0, self->ssl, ret); err = _PySSL_errno(ret < 0, self->ssl, ret);
PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS(self)
self->err = err; 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 */
@ -3375,9 +3384,10 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
// no other thread can be touching this object yet. // no other thread can be touching this object yet.
// (Technically, we can't even lock if we wanted to, as the // (Technically, we can't even lock if we wanted to, as the
// lock hasn't been initialized yet.) // lock hasn't been initialized yet.)
PySSL_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
ctx = SSL_CTX_new(method); ctx = SSL_CTX_new(method);
PySSL_END_ALLOW_THREADS Py_END_ALLOW_THREADS
_PySSL_FIX_ERRNO;
if (ctx == NULL) { if (ctx == NULL) {
_setSSLError(get_ssl_state(module), NULL, 0, __FILE__, __LINE__); _setSSLError(get_ssl_state(module), NULL, 0, __FILE__, __LINE__);
@ -3402,6 +3412,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
self->psk_client_callback = NULL; self->psk_client_callback = NULL;
self->psk_server_callback = NULL; self->psk_server_callback = NULL;
#endif #endif
self->tstate_mutex = (PyMutex){0};
/* Don't check host name by default */ /* Don't check host name by default */
if (proto_version == PY_SSL_VERSION_TLS_CLIENT) { if (proto_version == PY_SSL_VERSION_TLS_CLIENT) {
@ -3520,9 +3531,10 @@ context_clear(PyObject *op)
Py_CLEAR(self->psk_server_callback); Py_CLEAR(self->psk_server_callback);
#endif #endif
if (self->keylog_bio != NULL) { if (self->keylog_bio != NULL) {
PySSL_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
BIO_free_all(self->keylog_bio); BIO_free_all(self->keylog_bio);
PySSL_END_ALLOW_THREADS Py_END_ALLOW_THREADS
_PySSL_FIX_ERRNO;
self->keylog_bio = NULL; self->keylog_bio = NULL;
} }
return 0; return 0;
@ -4245,7 +4257,8 @@ _password_callback(char *buf, int size, int rwflag, void *userdata)
_PySSLPasswordInfo *pw_info = (_PySSLPasswordInfo*) userdata; _PySSLPasswordInfo *pw_info = (_PySSLPasswordInfo*) userdata;
PyObject *fn_ret = NULL; PyObject *fn_ret = NULL;
PySSL_END_ALLOW_THREADS_S(pw_info->thread_state); pw_info->thread_state = PyThreadState_Swap(pw_info->thread_state);
_PySSL_FIX_ERRNO;
if (pw_info->error) { if (pw_info->error) {
/* already failed previously. OpenSSL 3.0.0-alpha14 invokes the /* already failed previously. OpenSSL 3.0.0-alpha14 invokes the
@ -4275,13 +4288,13 @@ _password_callback(char *buf, int size, int rwflag, void *userdata)
goto error; goto error;
} }
PySSL_BEGIN_ALLOW_THREADS_S(pw_info->thread_state); pw_info->thread_state = PyThreadState_Swap(pw_info->thread_state);
memcpy(buf, pw_info->password, pw_info->size); memcpy(buf, pw_info->password, pw_info->size);
return pw_info->size; return pw_info->size;
error: error:
Py_XDECREF(fn_ret); Py_XDECREF(fn_ret);
PySSL_BEGIN_ALLOW_THREADS_S(pw_info->thread_state); pw_info->thread_state = PyThreadState_Swap(pw_info->thread_state);
pw_info->error = 1; pw_info->error = 1;
return -1; return -1;
} }
@ -4334,10 +4347,10 @@ _ssl__SSLContext_load_cert_chain_impl(PySSLContext *self, PyObject *certfile,
SSL_CTX_set_default_passwd_cb(self->ctx, _password_callback); SSL_CTX_set_default_passwd_cb(self->ctx, _password_callback);
SSL_CTX_set_default_passwd_cb_userdata(self->ctx, &pw_info); SSL_CTX_set_default_passwd_cb_userdata(self->ctx, &pw_info);
} }
PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state); PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state, &self->tstate_mutex);
r = SSL_CTX_use_certificate_chain_file(self->ctx, r = SSL_CTX_use_certificate_chain_file(self->ctx,
PyBytes_AS_STRING(certfile_bytes)); PyBytes_AS_STRING(certfile_bytes));
PySSL_END_ALLOW_THREADS_S(pw_info.thread_state); PySSL_END_ALLOW_THREADS_S(pw_info.thread_state, &self->tstate_mutex);
if (r != 1) { if (r != 1) {
if (pw_info.error) { if (pw_info.error) {
ERR_clear_error(); ERR_clear_error();
@ -4352,11 +4365,11 @@ _ssl__SSLContext_load_cert_chain_impl(PySSLContext *self, PyObject *certfile,
} }
goto error; goto error;
} }
PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state); PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state, &self->tstate_mutex);
r = SSL_CTX_use_PrivateKey_file(self->ctx, r = SSL_CTX_use_PrivateKey_file(self->ctx,
PyBytes_AS_STRING(keyfile ? keyfile_bytes : certfile_bytes), PyBytes_AS_STRING(keyfile ? keyfile_bytes : certfile_bytes),
SSL_FILETYPE_PEM); SSL_FILETYPE_PEM);
PySSL_END_ALLOW_THREADS_S(pw_info.thread_state); PySSL_END_ALLOW_THREADS_S(pw_info.thread_state, &self->tstate_mutex);
Py_CLEAR(keyfile_bytes); Py_CLEAR(keyfile_bytes);
Py_CLEAR(certfile_bytes); Py_CLEAR(certfile_bytes);
if (r != 1) { if (r != 1) {
@ -4373,9 +4386,9 @@ _ssl__SSLContext_load_cert_chain_impl(PySSLContext *self, PyObject *certfile,
} }
goto error; goto error;
} }
PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state); PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state, &self->tstate_mutex);
r = SSL_CTX_check_private_key(self->ctx); r = SSL_CTX_check_private_key(self->ctx);
PySSL_END_ALLOW_THREADS_S(pw_info.thread_state); PySSL_END_ALLOW_THREADS_S(pw_info.thread_state, &self->tstate_mutex);
if (r != 1) { if (r != 1) {
_setSSLError(get_state_ctx(self), NULL, 0, __FILE__, __LINE__); _setSSLError(get_state_ctx(self), NULL, 0, __FILE__, __LINE__);
goto error; goto error;
@ -4592,9 +4605,9 @@ _ssl__SSLContext_load_verify_locations_impl(PySSLContext *self,
cafile_buf = PyBytes_AS_STRING(cafile_bytes); cafile_buf = PyBytes_AS_STRING(cafile_bytes);
if (capath) if (capath)
capath_buf = PyBytes_AS_STRING(capath_bytes); capath_buf = PyBytes_AS_STRING(capath_bytes);
PySSL_BEGIN_ALLOW_THREADS PySSL_BEGIN_ALLOW_THREADS(self)
r = SSL_CTX_load_verify_locations(self->ctx, cafile_buf, capath_buf); r = SSL_CTX_load_verify_locations(self->ctx, cafile_buf, capath_buf);
PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS(self)
if (r != 1) { if (r != 1) {
if (errno != 0) { if (errno != 0) {
PyErr_SetFromErrno(PyExc_OSError); PyErr_SetFromErrno(PyExc_OSError);
@ -4646,10 +4659,11 @@ _ssl__SSLContext_load_dh_params_impl(PySSLContext *self, PyObject *filepath)
return NULL; return NULL;
errno = 0; errno = 0;
PySSL_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
dh = PEM_read_DHparams(f, NULL, NULL, NULL); dh = PEM_read_DHparams(f, NULL, NULL, NULL);
fclose(f); fclose(f);
PySSL_END_ALLOW_THREADS Py_END_ALLOW_THREADS
_PySSL_FIX_ERRNO;
if (dh == NULL) { if (dh == NULL) {
if (errno != 0) { if (errno != 0) {
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, filepath); PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, filepath);
@ -4801,6 +4815,7 @@ _ssl__SSLContext_set_default_verify_paths_impl(PySSLContext *self)
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
rc = SSL_CTX_set_default_verify_paths(self->ctx); rc = SSL_CTX_set_default_verify_paths(self->ctx);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
_PySSL_FIX_ERRNO;
if (!rc) { if (!rc) {
_setSSLError(get_state_ctx(self), NULL, 0, __FILE__, __LINE__); _setSSLError(get_state_ctx(self), NULL, 0, __FILE__, __LINE__);
return NULL; return NULL;

View file

@ -140,13 +140,15 @@ _PySSL_keylog_callback(const SSL *ssl, const char *line)
* critical debug helper. * critical debug helper.
*/ */
PySSL_BEGIN_ALLOW_THREADS assert(PyMutex_IsLocked(&ssl_obj->tstate_mutex));
Py_BEGIN_ALLOW_THREADS
PyThread_acquire_lock(lock, 1); PyThread_acquire_lock(lock, 1);
res = BIO_printf(ssl_obj->ctx->keylog_bio, "%s\n", line); res = BIO_printf(ssl_obj->ctx->keylog_bio, "%s\n", line);
e = errno; e = errno;
(void)BIO_flush(ssl_obj->ctx->keylog_bio); (void)BIO_flush(ssl_obj->ctx->keylog_bio);
PyThread_release_lock(lock); PyThread_release_lock(lock);
PySSL_END_ALLOW_THREADS Py_END_ALLOW_THREADS
_PySSL_FIX_ERRNO;
if (res == -1) { if (res == -1) {
errno = e; errno = e;
@ -187,9 +189,10 @@ _PySSLContext_set_keylog_filename(PyObject *op, PyObject *arg,
if (self->keylog_bio != NULL) { if (self->keylog_bio != NULL) {
BIO *bio = self->keylog_bio; BIO *bio = self->keylog_bio;
self->keylog_bio = NULL; self->keylog_bio = NULL;
PySSL_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
BIO_free_all(bio); BIO_free_all(bio);
PySSL_END_ALLOW_THREADS Py_END_ALLOW_THREADS
_PySSL_FIX_ERRNO;
} }
if (arg == Py_None) { if (arg == Py_None) {
@ -211,13 +214,13 @@ _PySSLContext_set_keylog_filename(PyObject *op, PyObject *arg,
self->keylog_filename = Py_NewRef(arg); self->keylog_filename = Py_NewRef(arg);
/* Write a header for seekable, empty files (this excludes pipes). */ /* Write a header for seekable, empty files (this excludes pipes). */
PySSL_BEGIN_ALLOW_THREADS PySSL_BEGIN_ALLOW_THREADS(self)
if (BIO_tell(self->keylog_bio) == 0) { if (BIO_tell(self->keylog_bio) == 0) {
BIO_puts(self->keylog_bio, BIO_puts(self->keylog_bio,
"# TLS secrets log file, generated by OpenSSL / Python\n"); "# TLS secrets log file, generated by OpenSSL / Python\n");
(void)BIO_flush(self->keylog_bio); (void)BIO_flush(self->keylog_bio);
} }
PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS(self)
SSL_CTX_set_keylog_callback(self->ctx, _PySSL_keylog_callback); SSL_CTX_set_keylog_callback(self->ctx, _PySSL_keylog_callback);
return 0; return 0;
} }