Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Segfault fromPy_DECREF(NULL) in_servername_callback error label in_ssl.c #146080

Open
Assignees
picnixz
Labels
3.13bugs and security fixes3.14bugs and security fixes3.15new features, bugs and security fixesextension-modulesC modules in the Modules dirtopic-SSLtype-crashA hard crash of the interpreter, possibly with a core dump
@devdanzin

Description

@devdanzin

Crash report

What happened?

It's possible to cause a segfault in_servername_callback error label by makingssl_socket to 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_socket isNULL. The error label at line 5197 callsPy_DECREF(ssl_socket) onNULL -> segfault.
Fix: ChangePy_DECREF toPy_XDECREF
File:Modules/_ssl.c, line 5197

Full report

WARNING: THIS MRE WILLRUN openssl IN A SUBPROCESS ANDLEAK THE CERT AND KEY FILES
MRE:

"""WARNING: THIS MRE WILL RUN openssl IN A SUBPROCESS AND LEAK THE CERT AND KEY FILESStrategy: Use SSLObject (not SSLSocket) so the Python-level wrapper canbe GC'd independently of the C-level SSL object. In the SNI callback,delete the only reference to the SSLObject and force GC. The weakrefin ssl->owner dies, PyWeakref_GetRef returns 0, ssl_socket = NULL,goto error -> Py_DECREF(NULL) -> crash."""importsslimportgcimporttempfileimportsubprocessdefgenerate_self_signed_cert():"""    Generate a self-signed cert+key for testing.    WARNING: THIS RUNS openssl IN A SUBPROCESS AND LEAKS THE CERT AND KEY FILES    """certpath=tempfile.mktemp(suffix='.pem')keypath=tempfile.mktemp(suffix='.key')subprocess.run(['openssl','req','-x509','-newkey','rsa:2048','-keyout',keypath,'-out',certpath,'-days','1','-nodes','-subj','/CN=test'    ],capture_output=True,check=True)returncertpath,keypathdefsni_callback(sslobj,servername,sslctx):passcertpath,keypath=generate_self_signed_cert()server_ctx=ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)server_ctx.load_cert_chain(certpath,keypath)server_ctx.set_servername_callback(sni_callback)# Approach: Use MemoryBIO-based SSLObject (not socket-based SSLSocket)# to have more control over the object lifecycle.server_incoming=ssl.MemoryBIO()server_outgoing=ssl.MemoryBIO()server_sslobj=server_ctx.wrap_bio(server_incoming,server_outgoing,server_side=True)client_ctx=ssl.create_default_context()client_ctx.check_hostname=Falseclient_ctx.verify_mode=ssl.CERT_NONEclient_incoming=ssl.MemoryBIO()client_outgoing=ssl.MemoryBIO()client_sslobj=client_ctx.wrap_bio(client_incoming,client_outgoing,server_side=False,server_hostname='test')# Get the internal _SSLSocket objectsserver_ssl_internal=server_sslobj._sslobjclient_ssl_internal=client_sslobj._sslobj# The _SSLSocket's "owner" weakref points to the SSLObject.# If we delete the SSLObject, the weakref dies.# Try to do the handshakeforiinrange(20):# Client steptry:client_sslobj.do_handshake()exceptssl.SSLWantReadError:pass# Transfer client -> serverdata=client_outgoing.read()ifdata:server_incoming.write(data)# NOW: before server processes the ClientHello (which triggers SNI),# try to kill the server SSLObject so the weakref dies.ifi==0anddata:# The server hasn't processed ClientHello yet.# Delete the SSLObject wrapper — the internal _SSLSocket# still exists (we hold server_ssl_internal).# The weakref ssl->owner should now be dead.delserver_sslobjgc.collect()print(f"server_ssl_internal still alive:{server_ssl_internalisnotNone}")# Now do_handshake on the internal object directly# This will trigger the SNI callback with a dead owner weakrefserver_ssl_internal.do_handshake()

Backtrace:

Program received signal SIGSEGV, Segmentation fault.0x00007bfff59bf197 in Py_DECREF (lineno=5197, op=0x0, filename=<optimized out>) at ./Include/refcount.h:390390         if (op->ob_refcnt_full <= 0 || op->ob_refcnt > (((PY_UINT32_T)-1) - (1<<20))) {#0  0x00007bfff59bf197 in Py_DECREF (lineno=5197, op=0x0, filename=<optimized out>) at ./Include/refcount.h:390#1  _servername_callback (s=0x7e1ff6ff8100, al=<optimized out>, args=0x7d4ff7011930) at ./Modules/_ssl.c:5197#2  0x00007bfff459d89a in ?? () from /lib/x86_64-linux-gnu/libssl.so.3#3  0x00007bfff459eddc in ?? () from /lib/x86_64-linux-gnu/libssl.so.3#4  0x00007bfff45c15f2 in ?? () from /lib/x86_64-linux-gnu/libssl.so.3#5  0x00007bfff45abdbb in ?? () from /lib/x86_64-linux-gnu/libssl.so.3#6  0x00007bfff59bff61 in _ssl__SSLSocket_do_handshake_impl (self=0x7caff70e4070) at ./Modules/_ssl.c:1052#7  _ssl__SSLSocket_do_handshake (self=0x7caff70e4070, _unused_ignored=<optimized out>) at ./Modules/clinic/_ssl.c.h:30#8  0x0000555555ae6992 in method_vectorcall_NOARGS (func=func@entry=0x7c7ff70f14c0, args=args@entry=0x7bfff5d8c728, nargsf=nargsf@entry=9223372036854775809, kwnames=kwnames@entry=0x0)    at Objects/descrobject.c:448#9  0x0000555555ab9e00 in _PyObject_VectorcallTstate (tstate=0x5555568f7b18 <_PyRuntime+360664>, callable=0x7c7ff70f14c0, args=0x7bfff5d8c728, nargsf=9223372036854775809, kwnames=0x0)    at ./Include/internal/pycore_call.h:136#10 0x0000555555e588dd in _Py_VectorCallInstrumentation_StackRefSteal (callable=..., arguments=<optimized out>, total_args=1, kwnames=..., call_instrumentation=<optimized out>,    frame=<optimized out>, this_instr=<optimized out>, tstate=<optimized out>) at Python/ceval.c:770#11 0x0000555555e94263 in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>) at Python/generated_cases.c.h:1838#12 0x0000555555e57778 in _PyEval_EvalFrame (tstate=0x5555568f7b18 <_PyRuntime+360664>, frame=0x7e8ff6fe5220, throwflag=0) at ./Include/internal/pycore_ceval.h:118#13 _PyEval_Vector (tstate=<optimized out>, func=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kwnames=0x0) at Python/ceval.c:2134#14 0x0000555555e57195 in PyEval_EvalCode (co=<optimized out>, globals=<optimized out>, locals=0x7c7ff70884c0) at Python/ceval.c:681#15 0x0000555556061fb0 in run_eval_code_obj (tstate=tstate@entry=0x5555568f7b18 <_PyRuntime+360664>, co=co@entry=0x7d8ff7016690, globals=globals@entry=0x7c7ff70884c0,    locals=locals@entry=0x7c7ff70884c0) at Python/pythonrun.c:1368#16 0x000055555606117c in run_mod (mod=<optimized out>, filename=<optimized out>, globals=<optimized out>, locals=<optimized out>, flags=<optimized out>, arena=<optimized out>,    interactive_src=<optimized out>, generate_new_source=<optimized out>) at Python/pythonrun.c:1471

Found usingcpython-review-toolkit with Claude Opus 4.6, using the/cpython-review-toolkit:explore Modules/_ssl.c all deep command.

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)]

Metadata

Metadata

Assignees

Labels

3.13bugs and security fixes3.14bugs and security fixes3.15new features, bugs and security fixesextension-modulesC modules in the Modules dirtopic-SSLtype-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


    [8]ページ先頭

    ©2009-2026 Movatter.jp