Skip to content

Commit 186f52f

Browse files
ZeroIntensitypicnixz
authored andcommitted
pythongh-124984: Enhance ssl thread safety (python#124993)
Make SSL objects thread safe in Free Theaded build by using critical sections. Co-authored-by: Bénédikt Tran <[email protected]>
1 parent f781a5d commit 186f52f

File tree

4 files changed

+1767
-315
lines changed

4 files changed

+1767
-315
lines changed

Lib/test/test_ssl.py

+51
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import unittest
55
import unittest.mock
66
from ast import literal_eval
7+
from threading import Thread
78
from test import support
89
from test.support import import_helper
910
from test.support import os_helper
@@ -277,11 +278,19 @@ def test_wrap_socket(sock, *,
277278
return context.wrap_socket(sock, **kwargs)
278279

279280

281+
USE_SAME_TEST_CONTEXT = False
282+
_TEST_CONTEXT = None
283+
280284
def testing_context(server_cert=SIGNED_CERTFILE, *, server_chain=True):
281285
"""Create context
282286
283287
client_context, server_context, hostname = testing_context()
284288
"""
289+
global _TEST_CONTEXT
290+
if USE_SAME_TEST_CONTEXT:
291+
if _TEST_CONTEXT is not None:
292+
return _TEST_CONTEXT
293+
285294
if server_cert == SIGNED_CERTFILE:
286295
hostname = SIGNED_CERTFILE_HOSTNAME
287296
elif server_cert == SIGNED_CERTFILE2:
@@ -299,6 +308,10 @@ def testing_context(server_cert=SIGNED_CERTFILE, *, server_chain=True):
299308
if server_chain:
300309
server_context.load_verify_locations(SIGNING_CA)
301310

311+
if USE_SAME_TEST_CONTEXT:
312+
if _TEST_CONTEXT is not None:
313+
_TEST_CONTEXT = client_context, server_context, hostname
314+
302315
return client_context, server_context, hostname
303316

304317

@@ -2801,6 +2814,44 @@ def test_echo(self):
28012814
'Cannot create a client socket with a PROTOCOL_TLS_SERVER context',
28022815
str(e.exception))
28032816

2817+
@unittest.skipUnless(support.Py_GIL_DISABLED, "test is only useful if the GIL is disabled")
2818+
def test_ssl_in_multiple_threads(self):
2819+
# See GH-124984: OpenSSL is not thread safe.
2820+
threads = []
2821+
2822+
global USE_SAME_TEST_CONTEXT
2823+
USE_SAME_TEST_CONTEXT = True
2824+
try:
2825+
for func in (
2826+
self.test_echo,
2827+
self.test_alpn_protocols,
2828+
self.test_getpeercert,
2829+
self.test_crl_check,
2830+
self.test_check_hostname_idn,
2831+
self.test_wrong_cert_tls12,
2832+
self.test_wrong_cert_tls13,
2833+
):
2834+
# Be careful with the number of threads here.
2835+
# Too many can result in failing tests.
2836+
for num in range(5):
2837+
with self.subTest(func=func, num=num):
2838+
threads.append(Thread(target=func))
2839+
2840+
with threading_helper.catch_threading_exception() as cm:
2841+
for thread in threads:
2842+
with self.subTest(thread=thread):
2843+
thread.start()
2844+
2845+
for thread in threads:
2846+
with self.subTest(thread=thread):
2847+
thread.join()
2848+
if cm.exc_value is not None:
2849+
# Some threads can skip their test
2850+
if not isinstance(cm.exc_value, unittest.SkipTest):
2851+
raise cm.exc_value
2852+
finally:
2853+
USE_SAME_TEST_CONTEXT = False
2854+
28042855
def test_getpeercert(self):
28052856
if support.verbose:
28062857
sys.stdout.write("\n")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed thread safety in :mod:`ssl` in the free-threaded build. OpenSSL operations are now protected by a per-object lock.

0 commit comments

Comments
 (0)