Skip to content

Commit 270490b

Browse files
colesburymiss-islington
authored andcommitted
gh-150191: Avoid data race in test_sni_callback_race (GH-150193)
(cherry picked from commit fd9d8c3) Co-authored-by: Sam Gross <colesbury@gmail.com>
1 parent 25a53d9 commit 270490b

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
@@ -1533,19 +1533,24 @@ def dummycallback(sock, servername, ctx, cycle=ctx):
15331533
gc.collect()
15341534
self.assertIs(wr(), None)
15351535

1536-
@unittest.skipUnless(support.Py_GIL_DISABLED,
1537-
"test is only useful if the GIL is disabled")
15381536
@threading_helper.requires_working_threading()
15391537
def test_sni_callback_race(self):
1540-
# Replacing sni_callback while handshakes are in-flight must not
1538+
# Replacing sni_callback while a handshake is in-flight must not
15411539
# crash (use-after-free on the callback in free-threaded builds).
1540+
#
1541+
# Use a single handshake thread: OpenSSL has internal data races
1542+
# on shared SSL_CTX state when multiple handshakes run
1543+
# concurrently against the same context (gh-150191). Concurrency
1544+
# on the *setter* is what exercises the fix from gh-149816, so
1545+
# multiple toggler threads race against each other and against
1546+
# the one handshake worker.
15421547
client_ctx, server_ctx, hostname = testing_context()
15431548

15441549
server_ctx.sni_callback = lambda *a: None
1545-
done = threading.Event()
1550+
deadline = time.monotonic() + 0.1
15461551

15471552
def do_handshakes():
1548-
while not done.is_set():
1553+
while time.monotonic() < deadline:
15491554
c_in = ssl.MemoryBIO()
15501555
c_out = ssl.MemoryBIO()
15511556
s_in = ssl.MemoryBIO()
@@ -1572,19 +1577,11 @@ def do_handshakes():
15721577
c_in.write(s_out.read())
15731578

15741579
def toggle_callback():
1575-
while not done.is_set():
1580+
while time.monotonic() < deadline:
15761581
server_ctx.sni_callback = lambda *a: None
15771582
server_ctx.sni_callback = None
15781583

1579-
workers = max(4, (os.cpu_count() or 4) * 2)
1580-
threads = [threading.Thread(target=do_handshakes)
1581-
for _ in range(workers)]
1582-
threads.append(threading.Thread(target=toggle_callback))
1583-
1584-
with threading_helper.catch_threading_exception() as cm:
1585-
with threading_helper.start_threads(threads):
1586-
done.set()
1587-
self.assertIsNone(cm.exc_value)
1584+
threading_helper.run_concurrently([do_handshakes] + 4 * [toggle_callback])
15881585

15891586
def test_cert_store_stats(self):
15901587
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)

0 commit comments

Comments
 (0)