https://github.com/python/cpython/commit/055e6b2891985b9ac88325c2bb544df9e3aa5a0d
commit: 055e6b2891985b9ac88325c2bb544df9e3aa5a0d
branch: 3.15
author: Miss Islington (bot) <[email protected]>
committer: cmaloney <[email protected]>
date: 2026-11-08T00:01:24Z
summary:

[3.15] gh-148891: prevent crash in HAMT caused by GC during traverse 
(GH-148928) (#161593)

gh-148891: prevent crash in HAMT caused by GC during traverse (GH-148928)

`_PyHamt_Assoc` allocates HAMT nodes, which can trigger GC. A weakref callback 
or finaliser fired during GC could re-enter `contextvar_set`, replacing 
`ctx->ctx_vars` via `Py_SETREF` and freeing the old HAMT while `_PyHamt_Assoc` 
was still traversing it, causing a use-after-free.

Hold a strong reference to `ctx->ctx_vars` during the `_PyHamt_Assoc` call, so 
that a re-entrant free cannot affect the HAMT being traversed.
(cherry picked from commit 1798d4fb3ca0e1cd8f9e6ab0ac99a0a9fb1f7527)

Co-authored-by: Thomas Kowalski <[email protected]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-04-23-08-36-47.gh-issue-148891.AMMHow.rst
M Python/context.c

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 29c483d2dc90d68..e4e58df0f96c98f 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;
     }

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to