diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-23-08-36-47.gh-issue-148891.AMMHow.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-23-08-36-47.gh-issue-148891.AMMHow.rst new file mode 100644 index 000000000000000..bae5c1366ca6a2c --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-23-08-36-47.gh-issue-148891.AMMHow.rst @@ -0,0 +1 @@ +Fix a crash in :func:`contextvars.Context.run` where a GC invocation could free the current HAMT during traverse. diff --git a/Python/context.c b/Python/context.c index d48543c9e023a61..65b3f25e700beb9 100644 --- a/Python/context.c +++ b/Python/context.c @@ -866,8 +866,13 @@ contextvar_set(PyContextVar *var, PyObject *val) return -1; } + // gh-148891: Make a strong reference so a GC during _PyHamt_Assoc will + // not free the HAMT during traversal. + PyHamtObject *old_vars = context_get_current_vars(ctx); + Py_INCREF(old_vars); PyHamtObject *new_vars = _PyHamt_Assoc( - context_get_current_vars(ctx), (PyObject *)var, val); + old_vars, (PyObject *)var, val); + Py_DECREF(old_vars); if (new_vars == NULL) { return -1; } @@ -894,8 +899,12 @@ contextvar_del(PyContextVar *var) return -1; } + // gh-148891: Make a strong reference so a GC during _PyHamt_Without will + // not free the HAMT during traversal. PyHamtObject *vars = context_get_current_vars(ctx); + Py_INCREF(vars); PyHamtObject *new_vars = _PyHamt_Without(vars, (PyObject *)var); + Py_DECREF(vars); if (new_vars == NULL) { return -1; }