https://github.com/python/cpython/commit/aee90465e55101ea1d14b28a2a5670b1802c2f18 commit: aee90465e55101ea1d14b28a2a5670b1802c2f18 branch: 3.14 author: Miss Islington (bot) <[email protected]> committer: cmaloney <[email protected]> date: 2026-11-07T23:57:31Z summary:
[3.14] gh-148891: prevent crash in HAMT caused by GC during traverse (GH-148928) (#161592) 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 13453a3ceab8448..9797545fb4da092 100644 --- a/Python/context.c +++ b/Python/context.c @@ -860,8 +860,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; } @@ -888,8 +893,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]
