Skip to content

Commit 2fa6836

Browse files
fix: prevent crash in hamt
1 parent d4eee16 commit 2fa6836

1 file changed

Lines changed: 13 additions & 1 deletion

File tree

Python/context.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -791,8 +791,16 @@ contextvar_set(PyContextVar *var, PyObject *val)
791791
return -1;
792792
}
793793

794+
// gh-148891: _PyHamt_Assoc can allocate HAMT nodes, which may trigger
795+
// GC. A weakref callback or finalizer could then re-enter
796+
// contextvar_set, replacing ctx->ctx_vars via Py_SETREF and freeing
797+
// the old HAMT while _PyHamt_Assoc is still traversing it.
798+
// Prevent this by holding a strong reference to the current vars.
799+
PyHamtObject *old_vars = ctx->ctx_vars;
800+
Py_INCREF(old_vars);
794801
PyHamtObject *new_vars = _PyHamt_Assoc(
795-
ctx->ctx_vars, (PyObject *)var, val);
802+
old_vars, (PyObject *)var, val);
803+
Py_DECREF(old_vars);
796804
if (new_vars == NULL) {
797805
return -1;
798806
}
@@ -819,8 +827,12 @@ contextvar_del(PyContextVar *var)
819827
return -1;
820828
}
821829

830+
// gh-148891: same borrowed-reference hazard as contextvar_set:
831+
// _PyHamt_Without allocates nodes, which may trigger GC re-entrancy.
822832
PyHamtObject *vars = ctx->ctx_vars;
833+
Py_INCREF(vars);
823834
PyHamtObject *new_vars = _PyHamt_Without(vars, (PyObject *)var);
835+
Py_DECREF(vars);
824836
if (new_vars == NULL) {
825837
return -1;
826838
}

0 commit comments

Comments
 (0)