Repository navigation
Segfault from Py_DECREF(NULL) in _servername_callback error label in _ssl.c #146080
Copy link
Copy link
Closed
Labels
3.13only security fixesonly security fixes3.14bugs and security fixesbugs and security fixes3.15bugs and security fixesbugs and security fixesextension-modulesC modules in the Modules dirC modules in the Modules dirtopic-SSLtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
Description
Activity
- addedtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dumpextension-modulesC modules in the Modules dirC modules in the Modules dir
on Mar 17, 2026 Is there a less intricate MRE so that we can have a simpler regression test?
I'll try to create one.
Reacted by Bénédikt TranThis is a bit better suited for a regression test, albeit still long:
import gc import ssl import test.support CERTFILE = test.support.findfile("keycert.pem", subdir="certdata") server_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) server_ctx.load_cert_chain(CERTFILE) server_ctx.set_servername_callback(lambda *a: None) client_ctx = ssl.create_default_context() client_ctx.check_hostname = False client_ctx.verify_mode = ssl.CERT_NONE server_in, server_out = ssl.MemoryBIO(), ssl.MemoryBIO() client_in, client_out = ssl.MemoryBIO(), ssl.MemoryBIO() server_sslobj = server_ctx.wrap_bio(server_in, server_out, server_side=True) client_sslobj = client_ctx.wrap_bio(client_in, client_out, server_side=False, server_hostname='test') # Keep a reference to the internal C-level _SSLSocket server_internal = server_sslobj._sslobj # Client sends ClientHello try: client_sslobj.do_handshake() except ssl.SSLWantReadError: pass server_in.write(client_out.read()) # Kill the SSLObject wrapper — the owner weakref in the C object dies del server_sslobj gc.collect() # Trigger the SNI callback with a dead owner weakref → Py_DECREF(NULL) server_internal.do_handshake() # crashes here
Is it good enough, or should I keep trying to get it smaller?
This one seems fine.
Do you want to make a PR or should I address it myself? I will have some time today so I can do it.
Please handle it, I'll be busy working on more reports.
I confirm that the following change does fix the crash:
diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 2eb31229a9b..61282484185 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -5194,7 +5194,7 @@ _servername_callback(SSL *s, int *al, void *args) return ret; error: - Py_DECREF(ssl_socket); + Py_XDECREF(ssl_socket); *al = SSL_AD_INTERNAL_ERROR; ret = SSL_TLSEXT_ERR_ALERT_FATAL; PyGILState_Release(gstate);
The non-trivial part is to write a regression test :-)
@picnixz plans to write a PR to fix this issue.
- added3.13only security fixesonly security fixes3.14bugs and security fixesbugs and security fixes3.15bugs and security fixesbugs and security fixes
on Mar 18, 2026 - added 5 commits that reference this issue
on Mar 29, 2026 Thanks for the report!
- added a commit that references this issue
on Mar 30, 2026
Metadata
Metadata
Assignees
Labels
3.13only security fixesonly security fixes3.14bugs and security fixesbugs and security fixes3.15bugs and security fixesbugs and security fixesextension-modulesC modules in the Modules dirC modules in the Modules dirtopic-SSLtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
Crash report
What happened?
It's possible to cause a segfault in
_servername_callbackerror label by makingssl_socketto beNULL.Automated diagnosis:
Bug:
Py_DECREF(NULL)crash in_ssl _servername_callback. When the SSL socket/owner weakref has been garbage collected during an SNI callback,ssl_socketisNULL. The error label at line 5197 callsPy_DECREF(ssl_socket)onNULL-> segfault.Fix: Change
Py_DECREFtoPy_XDECREFFile:
Modules/_ssl.c, line 5197Full report
WARNING: THIS MRE WILL RUN openssl IN A SUBPROCESS AND LEAK THE CERT AND KEY FILES
MRE:
Backtrace:
Found using cpython-review-toolkit with Claude Opus 4.6, using the
/cpython-review-toolkit:explore Modules/_ssl.c all deepcommand.CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
Python 3.15.0a7+ (heads/main:99e2c5eccd2, Mar 17 2026, 08:26:50) [Clang 21.1.2 (2ubuntu6)]
Linked PRs