Skip to content

[3.13] gh-124984: Enhance ssl thread safety (GH-124993) #125780

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions Lib/test/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import sys
import unittest
import unittest.mock
from ast import literal_eval
from threading import Thread
from test import support
from test.support import import_helper
from test.support import os_helper
Expand Down Expand Up @@ -304,11 +306,19 @@ def test_wrap_socket(sock, *,
return context.wrap_socket(sock, **kwargs)


USE_SAME_TEST_CONTEXT = False
_TEST_CONTEXT = None

def testing_context(server_cert=SIGNED_CERTFILE, *, server_chain=True):
"""Create context

client_context, server_context, hostname = testing_context()
"""
global _TEST_CONTEXT
if USE_SAME_TEST_CONTEXT:
if _TEST_CONTEXT is not None:
return _TEST_CONTEXT

if server_cert == SIGNED_CERTFILE:
hostname = SIGNED_CERTFILE_HOSTNAME
elif server_cert == SIGNED_CERTFILE2:
Expand All @@ -326,6 +336,10 @@ def testing_context(server_cert=SIGNED_CERTFILE, *, server_chain=True):
if server_chain:
server_context.load_verify_locations(SIGNING_CA)

if USE_SAME_TEST_CONTEXT:
if _TEST_CONTEXT is not None:
_TEST_CONTEXT = client_context, server_context, hostname

return client_context, server_context, hostname


Expand Down Expand Up @@ -2827,6 +2841,44 @@ def test_echo(self):
'Cannot create a client socket with a PROTOCOL_TLS_SERVER context',
str(e.exception))

@unittest.skipUnless(support.Py_GIL_DISABLED, "test is only useful if the GIL is disabled")
def test_ssl_in_multiple_threads(self):
# See GH-124984: OpenSSL is not thread safe.
threads = []

global USE_SAME_TEST_CONTEXT
USE_SAME_TEST_CONTEXT = True
try:
for func in (
self.test_echo,
self.test_alpn_protocols,
self.test_getpeercert,
self.test_crl_check,
self.test_check_hostname_idn,
self.test_wrong_cert_tls12,
self.test_wrong_cert_tls13,
):
# Be careful with the number of threads here.
# Too many can result in failing tests.
for num in range(5):
with self.subTest(func=func, num=num):
threads.append(Thread(target=func))

with threading_helper.catch_threading_exception() as cm:
for thread in threads:
with self.subTest(thread=thread):
thread.start()

for thread in threads:
with self.subTest(thread=thread):
thread.join()
if cm.exc_value is not None:
# Some threads can skip their test
if not isinstance(cm.exc_value, unittest.SkipTest):
raise cm.exc_value
finally:
USE_SAME_TEST_CONTEXT = False

def test_getpeercert(self):
if support.verbose:
sys.stdout.write("\n")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed thread safety in :mod:`ssl` in the free-threaded build. OpenSSL operations are now protected by a per-object lock.
Loading
Loading