Skip to content

SSLSocket.recv/SSLSocket.send concurrency crash #151508

Description

@divVerent

Crash report

What happened?

import os
import socket
import ssl
import threading
import time

os.system("openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -nodes -subj /CN=localhost")

def ssl_loop(func, stop_me):
    while not stop_me.is_set():
        try:
            func()
        except ssl.SSLError as e:
            if e.errno in (ssl.SSL_ERROR_WANT_READ, ssl.SSL_ERROR_WANT_WRITE):
                continue
            break
        except Exception:
            break

def run_ssl_server(server_sock, stop_event):
    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
    context.load_cert_chain(certfile="cert.pem", keyfile="key.pem")
    conn, _ = server_sock.accept()
    ssl_conn = context.wrap_socket(conn, server_side=True)
    def server_step():
        c.sendall(b"S" * 4096)
        c.recv(4096)
    ssl_loop(server_step, stop_event)

def try_to_crash():
    server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_sock.bind(('127.0.0.1', 0))
    server_sock.listen(5)
    stop_event = threading.Event()
    threading.Thread(target=run_ssl_server, args=(server_sock, stop_event), daemon=True).start()

    client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_sock.connect(server_sock.getsockname())
    context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
    context.check_hostname = False
    context.verify_mode = ssl.CERT_NONE

    ssl_client = context.wrap_socket(client_sock)
    ssl_client.settimeout(0.001)  # Can just retry if a short timeout is hit.

    stop_client = threading.Event()

    # Spawn 10 reader and writer threads against the same SSL client,
    # and let them run for one second.
    threads = []
    for _ in range(10):
        t_read = threading.Thread(target=lambda: ssl_loop(lambda: ssl_client.recv(4096), stop_client))
        t_write = threading.Thread(target=lambda: ssl_loop(lambda: ssl_client.sendall(b"Y" * 4096), stop_client))
        threads.extend([t_read, t_write])
        t_read.start()
        t_write.start()
    time.sleep(1)
    stop_event.set()
    for t in threads:
        t.join(timeout=0.1)
    ssl_client.close()
    server_sock.close()

for i in range(100):
    print(i)
    try_to_crash()
print('did not crash')

Usually crashes within about 10 seconds.

Backtrace:

* thread #9, name = 'python3', stop reason = signal SIGSEGV: sent by kernel (SI_KERNEL)
   * frame #0: 0x00007ffff7ca497d libc.so.6`_int_malloc(av=0x00007fffc0000030, bytes=21) at malloc.c:4217:14
     frame #1: 0x00007ffff7ca57f2 libc.so.6`__libc_malloc2(bytes=21) at malloc.c:3458:12
     frame #2: 0x00007ffff6a613ee libcrypto.so.3`CRYPTO_malloc at mem.c:214:11
     frame #3: 0x00007ffff6a06eb5 libcrypto.so.3`err_set_debug(es=0x00007fffc0005f60, i=1, file="../ssl/record/methods/tls_common.c", line=873, fn="tls_get_more_records") at err_local.h:69:33 [inlined]
     frame #4: 0x00007ffff6a06e70 libcrypto.so.3`ERR_set_debug(file="../ssl/record/methods/tls_common.c", line=873, func="tls_get_more_records") at err_blocks.c:37:5
     frame #5: 0x00007ffff70dfcf9 libssl.so.3`tls_get_more_records at tls_common.c:921:9
     frame #6: 0x00007ffff70de29a libssl.so.3`tls_read_record at tls_common.c:1141:15
     frame #7: 0x00007ffff70d6535 libssl.so.3`ssl3_read_bytes at rec_layer_s3.c:698:19
     frame #8: 0x00007ffff706ac40 libssl.so.3`ssl3_read_internal.part.0 at s3_lib.c:5131:11
     frame #9: 0x00007ffff7079f1d libssl.so.3`SSL_read_ex at ssl_lib.c:2388:15
     frame #10: 0x00007ffff71b0c37 _ssl.cpython-313-x86_64-linux-gnu.so`_ssl__SSLSocket_read_impl(self=0x00007ffff674c6a0, len=4096, group_right_1=<unavailable>, buffer=0x00007fffda7fbaa0) at _ssl.c:2622:18
     frame #11: 0x00007ffff71b0b51 _ssl.cpython-313-x86_64-linux-gnu.so`_ssl__SSLSocket_read(self=0x00007ffff674c6a0, args=<unavailable>) at _ssl.c.h:544:20
     frame #12: 0x0000000000560d03 python3`method_vectorcall_VARARGS(func=<unavailable>, args=<unavailable>, nargsf=<unavailable>, kwnames=<unavailable>) at descrobject.c:324:24
     frame #13: 0x0000000000559723 python3`_PyObject_VectorcallTstate(tstate=0x0000000000e248c0, callable=0x00007ffff727cc70, args=<unavailable>, nargsf=<unavailable>, kwnames=<unavailable>) at pycore_call.h:168:11 [inlined]
     frame #14: 0x0000000000559708 python3`PyObject_Vectorcall(callable=0x00007ffff727cc70, args=<unavailable>, nargsf=<unavailable>, kwnames=<unavailable>) at call.c:327:12
     frame #15: 0x000000000056ea15 python3`_PyEval_EvalFrameDefault(tstate=<unavailable>, frame=<unavailable>, throwflag=<unavailable>) at generated_cases.c.h:1850:23
     frame #16: 0x00000000005dd5ea python3`_PyObject_VectorcallTstate(tstate=0x0000000000e248c0, callable=0x00007ffff6755da0, args=0x00007fffda7fbdd8, nargsf=1, kwnames=0x0000000000000000) at pycore_call.h:168:11 [inlined]
     frame #17: 0x00000000005dd5b7 python3`method_vectorcall(method=<unavailable>, args=<unavailable>, nargsf=<unavailable>, kwnames=<unavailable>) at classobject.c:71:20
     frame #18: 0x00000000006f7c10 python3`thread_run(boot_raw=0x0000000000d47a60) at _threadmodule.c:343:21
     frame #19: 0x000000000069f908 python3`pythread_wrapper(arg=<unavailable>) at thread_pthread.h:242:5
     frame #20: 0x00007ffff7c95dc9 libc.so.6`start_thread(arg=<unavailable>) at pthread_create.c:448:8
     frame #21: 0x00007ffff7d14d88 libc.so.6`__clone3 at clone3.S:78

Cause is assuming that SSL_read and SSL_write functions in OpenSSL can be called concurrently (see #137583 (comment)).

It probably would make sense to either:

CPython versions tested on:

3.13, 3.14

Operating systems tested on:

Linux, macOS

Output from running 'python -VV' on the command line:

Python 3.13.5 (main, May 5 2026, 21:05:52) [GCC 14.2.0]

Activity

  1. added
    type-crashA hard crash of the interpreter, possibly with a core dump
    on Jun 15, 2026
  2. divVerent commented on Jun 15, 2026

    @divVerent
    Author

    Seems this basically demonstrates the issue about send/recv in #143756.

    That issue however mixes two different concurrency issues; not sure if that means this one should be a dupe, as this one is specifically about send/recv and the other is about the broader category.

  3. ZeroIntensity commented on Jun 15, 2026

    @ZeroIntensity
    Member

    I'm going to close this as a duplicate of #143756. Feel free to drop your repro there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-crashA hard crash of the interpreter, possibly with a core dump

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions