https://github.com/python/cpython/commit/fd9d8c3c66fc147cac6024c63b8c109d65e3aec8
commit: fd9d8c3c66fc147cac6024c63b8c109d65e3aec8
branch: main
author: Sam Gross <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-07-07T21:22:00+05:30
summary:
gh-150191: Avoid data race in test_sni_callback_race (#150193)
files:
M Lib/test/test_ssl.py
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 40111d41007795..575a322aa45923 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -1604,19 +1604,24 @@ def dummycallback(sock, servername, ctx, cycle=ctx):
gc.collect()
self.assertIs(wr(), None)
- @unittest.skipUnless(support.Py_GIL_DISABLED,
- "test is only useful if the GIL is disabled")
@threading_helper.requires_working_threading()
def test_sni_callback_race(self):
- # Replacing sni_callback while handshakes are in-flight must not
+ # Replacing sni_callback while a handshake is in-flight must not
# crash (use-after-free on the callback in free-threaded builds).
+ #
+ # Use a single handshake thread: OpenSSL has internal data races
+ # on shared SSL_CTX state when multiple handshakes run
+ # concurrently against the same context (gh-150191). Concurrency
+ # on the *setter* is what exercises the fix from gh-149816, so
+ # multiple toggler threads race against each other and against
+ # the one handshake worker.
client_ctx, server_ctx, hostname = testing_context()
server_ctx.sni_callback = lambda *a: None
- done = threading.Event()
+ deadline = time.monotonic() + 0.1
def do_handshakes():
- while not done.is_set():
+ while time.monotonic() < deadline:
c_in = ssl.MemoryBIO()
c_out = ssl.MemoryBIO()
s_in = ssl.MemoryBIO()
@@ -1643,19 +1648,11 @@ def do_handshakes():
c_in.write(s_out.read())
def toggle_callback():
- while not done.is_set():
+ while time.monotonic() < deadline:
server_ctx.sni_callback = lambda *a: None
server_ctx.sni_callback = None
- workers = max(4, (os.cpu_count() or 4) * 2)
- threads = [threading.Thread(target=do_handshakes)
- for _ in range(workers)]
- threads.append(threading.Thread(target=toggle_callback))
-
- with threading_helper.catch_threading_exception() as cm:
- with threading_helper.start_threads(threads):
- done.set()
- self.assertIsNone(cm.exc_value)
+ threading_helper.run_concurrently([do_handshakes] + 4 *
[toggle_callback])
def test_cert_store_stats(self):
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]