Skip to content

Commit 5ec6b3d

Browse files
committed
gh-150191: Avoid data race in test_sni_callback_race
Use a single handshake thread to avoid OpenSSL-internal data races on shared SSL_CTX state, while keeping multiple togglers to exercise the sni_callback setter race from gh-149816.
1 parent c35b0f2 commit 5ec6b3d

1 file changed

Lines changed: 12 additions & 15 deletions

File tree

Lib/test/test_ssl.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,19 +1606,24 @@ def dummycallback(sock, servername, ctx, cycle=ctx):
16061606
gc.collect()
16071607
self.assertIs(wr(), None)
16081608

1609-
@unittest.skipUnless(support.Py_GIL_DISABLED,
1610-
"test is only useful if the GIL is disabled")
16111609
@threading_helper.requires_working_threading()
16121610
def test_sni_callback_race(self):
1613-
# Replacing sni_callback while handshakes are in-flight must not
1611+
# Replacing sni_callback while a handshake is in-flight must not
16141612
# crash (use-after-free on the callback in free-threaded builds).
1613+
#
1614+
# Use a single handshake thread: OpenSSL has internal data races
1615+
# on shared SSL_CTX state when multiple handshakes run
1616+
# concurrently against the same context (gh-150191). Concurrency
1617+
# on the *setter* is what exercises the fix from gh-149816, so
1618+
# multiple toggler threads race against each other and against
1619+
# the one handshake worker.
16151620
client_ctx, server_ctx, hostname = testing_context()
16161621

16171622
server_ctx.sni_callback = lambda *a: None
1618-
done = threading.Event()
1623+
deadline = time.monotonic() + 0.1
16191624

16201625
def do_handshakes():
1621-
while not done.is_set():
1626+
while time.monotonic() < deadline:
16221627
c_in = ssl.MemoryBIO()
16231628
c_out = ssl.MemoryBIO()
16241629
s_in = ssl.MemoryBIO()
@@ -1645,19 +1650,11 @@ def do_handshakes():
16451650
c_in.write(s_out.read())
16461651

16471652
def toggle_callback():
1648-
while not done.is_set():
1653+
while time.monotonic() < deadline:
16491654
server_ctx.sni_callback = lambda *a: None
16501655
server_ctx.sni_callback = None
16511656

1652-
workers = max(4, (os.cpu_count() or 4) * 2)
1653-
threads = [threading.Thread(target=do_handshakes)
1654-
for _ in range(workers)]
1655-
threads.append(threading.Thread(target=toggle_callback))
1656-
1657-
with threading_helper.catch_threading_exception() as cm:
1658-
with threading_helper.start_threads(threads):
1659-
done.set()
1660-
self.assertIsNone(cm.exc_value)
1657+
threading_helper.run_concurrently([do_handshakes] + 4 * [toggle_callback])
16611658

16621659
def test_cert_store_stats(self):
16631660
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

0 commit comments

Comments
 (0)