Skip to content

Request 3.4.1 patch release for PyGreenlet_GetCurrent shutdown crash #524

Description

@taegyunkim

Summary

Would you consider publishing greenlet 3.4.1 with the PyGreenlet_GetCurrent() error-setting fix that is already present in 3.5.0?

I realize #508 intentionally became 3.5.0 because removing the atexit callback changed shutdown behavior. I am not asking to backport that broader behavior change. The requested 3.4.1 change is only this line from 004e1e9:

 static PyGreenlet*
 PyGreenlet_GetCurrent(void)
 {
     if (greenlet::IsShuttingDown()) {
+        PyErr_SetString(PyExc_RuntimeError, "greenlet is being finalized");
         return nullptr;
     }

We have observed crashes in services using greenlet 3.4.0. Upgrading to the 3.5 line resolves the issue, but the fix itself appears small enough to backport to the 3.4 line without bringing in the broader shutdown behavior changes from 3.5. A 3.4.1 patch release could therefore provide a narrowly scoped fix for users who have not yet upgraded.

Reproducer

Environment:

  • CPython 3.14.6
  • gevent 26.4.0
  • greenlet 3.4.0
  • reproduced on x86_64; the original production family also occurs on aarch64
from gevent import monkey
monkey.patch_all()
from gevent.local import local


class X:
    def __init__(self):
        self.local = local()
        self.local.value = 42

    def __del__(self):
        self.local.value


x = X()

For example:

$ uv venv --python 3.14.6 .venv
$ uv pip install --python .venv/bin/python gevent==26.4.0 greenlet==3.4.0
$ .venv/bin/python repro.py
Segmentation fault (core dumped)
$ echo $?
139

This reproduces 5/5 times here. No tracing or instrumentation library is imported.

The native stack ends in:

PyException_GetTraceback
PyTraceBack_Here
__Pyx_AddTraceback
__pyx_f_6gevent_14_gevent_clocal__local_get_dict
__pyx_tp_getattro_6gevent_14_gevent_clocal_local
PyObject_GetAttr
...
finalize_modules
_Py_Finalize

Root cause

During finalization, greenlet 3.4.0 correctly returns NULL from PyGreenlet_GetCurrent(), but it does not set an exception. gevent's Cython code follows the NULL error path and calls __Pyx_AddTraceback; CPython then enters traceback handling without a current exception and segfaults.

The one-line fix gives the NULL return the normal C API error contract. The result is the same controlled unraisable RuntimeError: greenlet is being finalized produced by greenlet 3.5.0 instead of a process crash.

Validation

  • greenlet 3.4.0: segfaulted 5/5 times
  • greenlet 3.4.0 plus the one-line patch: exited normally 10/10 times
  • greenlet 3.5.0: exited normally 5/5 times

Both patched 3.4.0 and 3.5.0 produce the same unraisable RuntimeError from the finalizer.

If a 3.4 maintenance branch is created, I would be happy to submit the backport and regression test as a PR. Binary wheels matching the 3.4.0 platform matrix would be especially helpful for pinned deployments.

Related: #507, #508.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions